From c6e42edf875639ad6dfd85db84beea1c2a2e8292 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Wed, 24 Dec 2025 13:51:26 +0100 Subject: [PATCH] update org-ref --- .../citeproc/csl-locales/locales-en-US.xml | 0 .../citeproc/csl-styles/apa-5th-edition.csl | 0 .../apa-numeric-superscript-brackets.csl | 0 .../chicago-author-date-16th-edition.csl | 0 ...ago-fullnote-bibliography-16th-edition.csl | 1368 +++++++++++++++++ .../citeproc/csl-styles/elsevier-harvard.csl | 0 .../csl-styles/elsevier-with-titles.csl | 0 .../contrib.el | 2 +- .../doi-utils.el | 123 +- .../nist-webbook.el | 0 .../openalex.el | 301 ++-- .../org-ref-arxiv.el | 42 +- .../org-ref-autoloads.el | 4 +- .../org-ref-bibliography-links.el | 4 +- .../org-ref-bibtex.el | 288 ++-- .../org-ref-citation-links.el | 174 ++- .../org-ref-compat.el | 0 .../org-ref-core.el | 171 ++- .../org-ref-export.el | 400 ++++- .../org-ref-extract.el | 14 +- .../org-ref-glossary.el | 345 ++++- .../org-ref-helm.el | 0 .../org-ref-isbn.el | 23 +- .../org-ref-ivy.el | 15 +- .../org-ref-label-link.el | 2 + .../org-ref-latex.el | 78 +- .../org-ref-misc-links.el | 4 +- .../org-ref-natbib-bbl-citeproc.el | 9 +- .../org-ref-pdf.el | 22 +- .../org-ref-pgk.el | 2 +- .../org-ref-pkg.el | 11 +- .../org-ref-publish.el | 4 +- .../org-ref-pubmed.el | 3 +- .../org-ref-ref-links.el | 464 +++++- .../org-ref-refproc.el | 8 +- .../org-ref-sci-id.el | 0 .../org-ref-scifinder.el | 0 .../org-ref-scopus.el | 57 +- .../org-ref-url-utils.el | 26 +- .../org-ref-utils.el | 225 ++- .../org-ref-worldcat.el | 0 .../org-ref-wos.el | 5 +- .../org-ref.bib | 0 .../org-ref.el | 8 +- .../org-ref.org | 26 +- .../x2bib.el | 2 + 46 files changed, 3419 insertions(+), 811 deletions(-) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/citeproc/csl-locales/locales-en-US.xml (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/citeproc/csl-styles/apa-5th-edition.csl (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/citeproc/csl-styles/apa-numeric-superscript-brackets.csl (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/citeproc/csl-styles/chicago-author-date-16th-edition.csl (100%) create mode 100644 elpa/org-ref-20251206.1422/citeproc/csl-styles/chicago-fullnote-bibliography-16th-edition.csl rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/citeproc/csl-styles/elsevier-harvard.csl (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/citeproc/csl-styles/elsevier-with-titles.csl (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/contrib.el (97%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/doi-utils.el (93%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/nist-webbook.el (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/openalex.el (84%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-arxiv.el (89%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-autoloads.el (99%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-bibliography-links.el (98%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-bibtex.el (86%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-citation-links.el (91%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-compat.el (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-core.el (66%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-export.el (62%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-extract.el (94%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-glossary.el (68%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-helm.el (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-isbn.el (92%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-ivy.el (89%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-label-link.el (96%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-latex.el (76%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-misc-links.el (98%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-natbib-bbl-citeproc.el (98%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-pdf.el (88%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-pgk.el (93%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-pkg.el (70%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-publish.el (93%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-pubmed.el (99%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-ref-links.el (54%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-refproc.el (98%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-sci-id.el (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-scifinder.el (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-scopus.el (80%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-url-utils.el (93%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-utils.el (84%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-worldcat.el (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref-wos.el (97%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref.bib (100%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref.el (87%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/org-ref.org (97%) rename elpa/{org-ref-20241124.2012 => org-ref-20251206.1422}/x2bib.el (98%) diff --git a/elpa/org-ref-20241124.2012/citeproc/csl-locales/locales-en-US.xml b/elpa/org-ref-20251206.1422/citeproc/csl-locales/locales-en-US.xml similarity index 100% rename from elpa/org-ref-20241124.2012/citeproc/csl-locales/locales-en-US.xml rename to elpa/org-ref-20251206.1422/citeproc/csl-locales/locales-en-US.xml diff --git a/elpa/org-ref-20241124.2012/citeproc/csl-styles/apa-5th-edition.csl b/elpa/org-ref-20251206.1422/citeproc/csl-styles/apa-5th-edition.csl similarity index 100% rename from elpa/org-ref-20241124.2012/citeproc/csl-styles/apa-5th-edition.csl rename to elpa/org-ref-20251206.1422/citeproc/csl-styles/apa-5th-edition.csl diff --git a/elpa/org-ref-20241124.2012/citeproc/csl-styles/apa-numeric-superscript-brackets.csl b/elpa/org-ref-20251206.1422/citeproc/csl-styles/apa-numeric-superscript-brackets.csl similarity index 100% rename from elpa/org-ref-20241124.2012/citeproc/csl-styles/apa-numeric-superscript-brackets.csl rename to elpa/org-ref-20251206.1422/citeproc/csl-styles/apa-numeric-superscript-brackets.csl diff --git a/elpa/org-ref-20241124.2012/citeproc/csl-styles/chicago-author-date-16th-edition.csl b/elpa/org-ref-20251206.1422/citeproc/csl-styles/chicago-author-date-16th-edition.csl similarity index 100% rename from elpa/org-ref-20241124.2012/citeproc/csl-styles/chicago-author-date-16th-edition.csl rename to elpa/org-ref-20251206.1422/citeproc/csl-styles/chicago-author-date-16th-edition.csl diff --git a/elpa/org-ref-20251206.1422/citeproc/csl-styles/chicago-fullnote-bibliography-16th-edition.csl b/elpa/org-ref-20251206.1422/citeproc/csl-styles/chicago-fullnote-bibliography-16th-edition.csl new file mode 100644 index 00000000..862ad975 --- /dev/null +++ b/elpa/org-ref-20251206.1422/citeproc/csl-styles/chicago-fullnote-bibliography-16th-edition.csl @@ -0,0 +1,1368 @@ + + diff --git a/elpa/org-ref-20241124.2012/citeproc/csl-styles/elsevier-harvard.csl b/elpa/org-ref-20251206.1422/citeproc/csl-styles/elsevier-harvard.csl similarity index 100% rename from elpa/org-ref-20241124.2012/citeproc/csl-styles/elsevier-harvard.csl rename to elpa/org-ref-20251206.1422/citeproc/csl-styles/elsevier-harvard.csl diff --git a/elpa/org-ref-20241124.2012/citeproc/csl-styles/elsevier-with-titles.csl b/elpa/org-ref-20251206.1422/citeproc/csl-styles/elsevier-with-titles.csl similarity index 100% rename from elpa/org-ref-20241124.2012/citeproc/csl-styles/elsevier-with-titles.csl rename to elpa/org-ref-20251206.1422/citeproc/csl-styles/elsevier-with-titles.csl diff --git a/elpa/org-ref-20241124.2012/contrib.el b/elpa/org-ref-20251206.1422/contrib.el similarity index 97% rename from elpa/org-ref-20241124.2012/contrib.el rename to elpa/org-ref-20251206.1422/contrib.el index f726f755..f1dcec00 100644 --- a/elpa/org-ref-20241124.2012/contrib.el +++ b/elpa/org-ref-20251206.1422/contrib.el @@ -19,7 +19,7 @@ (defun org-ref-get-bibtex-key-under-cursor--display () "Return key under the cursor in `org-mode'. If not on a key, but on a cite, prompt for key." - (if-let ((key (get-text-property (point) 'cite-key))) + (if-let* ((key (get-text-property (point) 'cite-key))) ;; Point is on a key, so we get it directly key ;; point is not on a key, but may still be on a cite link diff --git a/elpa/org-ref-20241124.2012/doi-utils.el b/elpa/org-ref-20251206.1422/doi-utils.el similarity index 93% rename from elpa/org-ref-20241124.2012/doi-utils.el rename to elpa/org-ref-20251206.1422/doi-utils.el index ddde49ab..8cd8de44 100644 --- a/elpa/org-ref-20241124.2012/doi-utils.el +++ b/elpa/org-ref-20251206.1422/doi-utils.el @@ -1,4 +1,4 @@ -;;; doi-utils.el --- DOI utilities for making bibtex entries +;;; doi-utils.el --- DOI utilities for making bibtex entries -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2021 John Kitchin @@ -41,15 +41,14 @@ (declare-function bibtex-completion-edit-notes "bibtex-completion") (declare-function org-bibtex-yank "org-bibtex") (declare-function org-ref-possible-bibfiles "org-ref-core") +(declare-function org-ref-normalize-bibtex-completion-bibliography "org-ref-utils") -(declare-function f-ext? "f") -(declare-function f-entries "f") -(declare-function s-match "s") +(declare-function org-ref--file-ext-p "org-ref-utils") +(declare-function org-ref--directory-files "org-ref-utils") (eval-when-compile (require 'cl-lib)) (require 'bibtex) -(require 'dash) (require 'json) (require 'org) ; org-add-link-type @@ -59,7 +58,7 @@ (require 'url-http) (require 'url-handlers) (require 'org-ref-utils) -(require 'hydra) +(require 'transient) ;;* Customization (defgroup doi-utils nil @@ -120,6 +119,35 @@ If nil use `doi-utils-get-bibtex-entry-pdf' synchronously." :group 'doi-utils) +(defun doi-utils-pdf-filename-from-key () + "Generate PDF filename from bibtex entry key. +This is the default function for `doi-utils-pdf-filename-function'. +Returns the bibtex entry key as the filename (without extension or path)." + (cdr (assoc "=key=" (bibtex-parse-entry)))) + + +(defcustom doi-utils-pdf-filename-function + 'doi-utils-pdf-filename-from-key + "Function to generate PDF filename from bibtex entry. +The function is called with no arguments while point is in the bibtex +entry, and should return a string to use as the PDF filename (without +the .pdf extension or directory path). + +The directory path is determined separately by `bibtex-completion-library-path'. + +The default function uses the bibtex entry key as the filename. + +Example: To use the title field as the filename: + (setq doi-utils-pdf-filename-function + (lambda () (bibtex-autokey-get-field \"title\"))) + +Note: The function is responsible for ensuring the returned filename +is valid for the filesystem. Special characters in fields like title +may cause issues on some systems." + :type 'function + :group 'doi-utils) + + ;;* Getting pdf files from a DOI ;; The idea here is simple. When you visit http://dx.doi.org/doi or @@ -308,7 +336,7 @@ https://onlinelibrary.wiley.com/doi/pdfdirect/10.1002/anie.201310461?download=tr (setq p2 (replace-regexp-in-string "^http\\(s?\\)://scitation.aip.org/" "" *doi-utils-redirect*)) (setq s (split-string p2 "/")) - (setq p1 (mapconcat 'identity (-remove-at-indices '(0 6) s) "/")) + (setq p1 (mapconcat 'identity (org-ref--remove-at-indices '(0 6) s) "/")) (setq p3 (concat "/" (nth 0 s) (nth 1 s) "/" (nth 2 s) "/" (nth 3 s))) (format "http://scitation.aip.org/deliver/fulltext/%s.pdf?itemId=/%s&mimeType=pdf&containerItemId=%s" p1 p2 p3)))) @@ -334,7 +362,7 @@ https://onlinelibrary.wiley.com/doi/pdfdirect/10.1002/anie.201310461?download=tr (defun ecs-pdf-url (*doi-utils-redirect*) "Get url to the pdf from *DOI-UTILS-REDIRECT*." (when (string-match "^http\\(s?\\)://jes.ecsdl.org" *doi-utils-redirect*) - (replace-regexp-in-string "\.abstract$" ".full.pdf" *doi-utils-redirect*))) + (replace-regexp-in-string "\\.abstract$" ".full.pdf" *doi-utils-redirect*))) ;; http://ecst.ecsdl.org/content/25/2/2769 ;; http://ecst.ecsdl.org/content/25/2/2769.full.pdf @@ -762,7 +790,7 @@ too. " (doi (replace-regexp-in-string "https?://\\(dx.\\)?.doi.org/" "" (bibtex-autokey-get-field "doi"))) - (key (cdr (assoc "=key=" (bibtex-parse-entry)))) + (base-name (funcall doi-utils-pdf-filename-function)) (pdf-url) (pdf-file)) @@ -774,7 +802,7 @@ too. " (car bibtex-completion-library-path)) (t (completing-read "Dir: " bibtex-completion-library-path))) - key ".pdf")) + base-name ".pdf")) (unless doi (error "No DOI found to get a pdf for")) @@ -834,7 +862,7 @@ checked." (doi (replace-regexp-in-string "https?://\\(dx.\\)?.doi.org/" "" (bibtex-autokey-get-field "doi"))) - (key (cdr (assoc "=key=" (bibtex-parse-entry)))) + (base-name (funcall doi-utils-pdf-filename-function)) (pdf-url) (pdf-file)) @@ -846,7 +874,7 @@ checked." (car bibtex-completion-library-path)) (t (completing-read "Dir: " bibtex-completion-library-path))) - key ".pdf")) + base-name ".pdf")) ;; now get file if needed. (unless (file-exists-p pdf-file) (cond @@ -900,7 +928,7 @@ every field.") (defun doi-utils-get-json-metadata (doi) "Try to get json metadata for DOI. Open the DOI in a browser if we do not get it." - (if-let ((data (cdr (assoc doi doi-utils-cache)))) + (if-let* ((data (cdr (assoc doi doi-utils-cache)))) ;; We have the data already, so we return it. data (let ((url-request-method "GET") @@ -1055,7 +1083,7 @@ MATCHING-TYPES." fields) (concat ,@(doi-utils-concat-prepare - (-flatten + (org-ref--flatten-list (list (concat "@" (symbol-name name) "{,\n") ;; there seems to be some bug with mapcan, ;; so we fall back to flatten @@ -1096,7 +1124,7 @@ MATCHING-TYPES." (let* ((results (funcall doi-utils-metadata-function doi)) (type (plist-get results :type))) ;; (format "%s" results) ; json-data - (or (-some (lambda (g) (funcall g type results)) doi-utils-bibtex-type-generators) + (or (cl-some (lambda (g) (funcall g type results)) doi-utils-bibtex-type-generators) (message "%s not supported yet\n%S." type results)))) ;; That is just the string for the entry. To be useful, we need a function that @@ -1171,31 +1199,31 @@ Argument BIBFILE the bibliography to use." ;; DOI raw ;; Ex: 10.1109/MALWARE.2014.6999410 ((and (stringp the-active-region) - (s-match (concat "^" doi-regexp) the-active-region)) + (org-ref--string-match (concat "^" doi-regexp) the-active-region)) the-active-region) ;; DOI url ;; Ex: https://doi.org/10.1109/MALWARE.2014.6999410 ((and (stringp the-active-region) - (s-match (concat doi-url-prefix-regexp doi-regexp) the-active-region)) + (org-ref--string-match (concat doi-url-prefix-regexp doi-regexp) the-active-region)) (replace-regexp-in-string doi-url-prefix-regexp "" the-active-region)) ;; DOI url as customized ((and (stringp the-active-region) - (s-match (regexp-quote doi-utils-dx-doi-org-url) the-active-region)) + (org-ref--string-match (regexp-quote doi-utils-dx-doi-org-url) the-active-region)) (replace-regexp-in-string (regexp-quote doi-utils-dx-doi-org-url) "" the-active-region)) ;; Check if DOI can be found in the current kill ;; DOI raw ;; Ex: 10.1109/MALWARE.2014.6999410 ((and (stringp the-current-kill) - (s-match (concat "^" doi-regexp) the-current-kill)) + (org-ref--string-match (concat "^" doi-regexp) the-current-kill)) the-current-kill) ;; DOI url ;; Ex: https://doi.org/10.1109/MALWARE.2014.6999410 ((and (stringp the-current-kill) - (s-match (concat doi-url-prefix-regexp doi-regexp) the-current-kill)) + (org-ref--string-match (concat doi-url-prefix-regexp doi-regexp) the-current-kill)) (replace-regexp-in-string doi-url-prefix-regexp "" the-current-kill)) ;; DOI url as customized ((and (stringp the-current-kill) - (s-match (regexp-quote doi-utils-dx-doi-org-url) the-current-kill)) + (org-ref--string-match (regexp-quote doi-utils-dx-doi-org-url) the-current-kill)) (replace-regexp-in-string (regexp-quote doi-utils-dx-doi-org-url) "" the-current-kill)) ;; otherwise, return nil (t @@ -1251,7 +1279,7 @@ Optional argument NODELIM see `bibtex-make-field'." (defun plist-get-keys (plist) "Return keys in a PLIST." - (-slice plist 0 nil 2)) + (cl-loop for (key _value) on plist by #'cddr collect key)) ;;;###autoload @@ -1415,9 +1443,7 @@ May be empty if none are found." (defun doi-utils-open-bibtex (doi) "Search through variable `bibtex-completion-bibliography' for DOI." (interactive "sDOI: ") - (cl-loop for f in (if (listp bibtex-completion-bibliography) - bibtex-completion-bibliography - (list bibtex-completion-bibliography)) + (cl-loop for f in (org-ref-normalize-bibtex-completion-bibliography) when (progn (find-file f) (when (search-forward doi (point-max) t) (bibtex-beginning-of-entry))) @@ -1453,23 +1479,40 @@ May be empty if none are found." (declare-function org-element-property "org-element") -(defhydra doi-link-follow (:color blue :hint nil) - "DOI actions: -" - ("o" (doi-utils-open (org-element-property :path (org-element-context))) "open") - ("w" (doi-utils-wos (org-element-property :path (org-element-context))) "wos") - ("c" (doi-utils-wos-citing (org-element-property :path (org-element-context))) "wos citing articles") - ("r" (doi-utils-wos-related (org-element-property :path (org-element-context))) "wos related articles" ) - ("a" (doi-utils-ads (org-element-property :path (org-element-context))) "ads") - ("s" (doi-utils-google-scholar (org-element-property :path (org-element-context))) "Google Scholar") - ("f" (doi-utils-crossref (org-element-property :path (org-element-context))) "CrossRef") - ("p" (doi-utils-pubmed (org-element-property :path (org-element-context))) "Pubmed") - ("b" (doi-utils-open-bibtex (org-element-property :path (org-element-context))) "open in bibtex") - ("g" (doi-utils-add-bibtex-entry-from-doi (org-element-property :path (org-element-context))) "get bibtex entry")) +(defun doi-utils--context-doi () + (org-element-property :path (org-element-context))) + +(transient-define-prefix doi-link-follow-menu () + "DOI actions." + [["Actions" + ("o" "open" (lambda () (interactive) + (doi-utils-open (doi-utils--context-doi)))) + ("w" "wos" (lambda () (interactive) + (doi-utils-wos (doi-utils--context-doi)))) + ("c" "wos citing articles" (lambda () (interactive) + (doi-utils-wos-citing (doi-utils--context-doi)))) + ("r" "wos related articles" (lambda () (interactive) + (doi-utils-wos-related (doi-utils--context-doi)))) + ("a" "ads" (lambda () (interactive) + (doi-utils-ads (doi-utils--context-doi)))) + ("s" "Google Scholar" (lambda () (interactive) + (doi-utils-google-scholar (doi-utils--context-doi)))) + ("f" "CrossRef" (lambda () (interactive) + (doi-utils-crossref (doi-utils--context-doi)))) + ("p" "Pubmed" (lambda () (interactive) + (doi-utils-pubmed (doi-utils--context-doi)))) + ("b" "open in bibtex" (lambda () (interactive) + (doi-utils-open-bibtex (doi-utils--context-doi)))) + ("g" "get bibtex entry" (lambda () (interactive) + (doi-utils-add-bibtex-entry-from-doi (doi-utils--context-doi)))) + ("q" "quit" transient-quit-one)]]) + +(define-obsolete-function-alias 'doi-link-follow/body + #'doi-link-follow-menu "3.1") (org-link-set-parameters "doi" - :follow (lambda (_) (doi-link-follow/body)) + :follow (lambda (_) (doi-link-follow-menu)) :export (lambda (doi desc format) (cond ((eq format 'html) @@ -1639,7 +1682,7 @@ Get a list of possible matches. Choose one with completion." nil))) (completing-read "Bibfile: " - (append (f-entries "." (lambda (f) (f-ext? f "bib"))) + (append (org-ref--directory-files "." (lambda (f) (org-ref--file-ext-p f "bib"))) bibtex-completion-bibliography)))) (let* ((json-data (with-temp-buffer (url-insert diff --git a/elpa/org-ref-20241124.2012/nist-webbook.el b/elpa/org-ref-20251206.1422/nist-webbook.el similarity index 100% rename from elpa/org-ref-20241124.2012/nist-webbook.el rename to elpa/org-ref-20251206.1422/nist-webbook.el diff --git a/elpa/org-ref-20241124.2012/openalex.el b/elpa/org-ref-20251206.1422/openalex.el similarity index 84% rename from elpa/org-ref-20241124.2012/openalex.el rename to elpa/org-ref-20251206.1422/openalex.el index 78bcd796..7e7768ff 100644 --- a/elpa/org-ref-20241124.2012/openalex.el +++ b/elpa/org-ref-20251206.1422/openalex.el @@ -1,4 +1,4 @@ -;;; openalex.el --- Org-ref interface to OpenAlex +;;; openalex.el --- Org-ref interface to OpenAlex -*- lexical-binding: t; -*- ;;; Commentary: ;; This is an elisp interface to OpenAlex (https://docs.openalex.org/) for org-ref. @@ -25,14 +25,13 @@ ;; `user-mail-address' value will be added to the queries if it exists so you ;; will get the polite pool. ;; -;; This library extends the `org-ref-citation-hydra' and adds keys to get to +;; This library extends the `org-ref-citation-menu' and adds keys to get to ;; cited by, references and related documents in OpenAlex. -(require 'dash) -(require 's) (require 'request) (require 'doi-utils) (require 'org-ref-citation-links) +(require 'org-ref-utils) (declare-function org-ref-possible-bibfiles "org-ref-core") (declare-function org-ref-find-bibliography "org-ref-core") @@ -40,6 +39,7 @@ (declare-function bibtex-completion-show-entry "bibtex-completion") (declare-function bibtex-completion-apa-format-reference "bibtex-completion") (declare-function ivy-read "ivy") +(declare-function ivy-more-chars "ivy") (defcustom oa-api-key nil @@ -59,6 +59,22 @@ (json-read))) +(defun oa--params (&rest params) + "Build API request parameters, excluding nil values. +PARAMS should be an alist of (key . value) pairs. +The mailto and api_key parameters are added automatically." + (let ((result (when user-mail-address + `(("mailto" . ,user-mail-address))))) + ;; Only add api_key if it's set and non-empty + (when (and oa-api-key (not (string-empty-p oa-api-key))) + (push `("api_key" . ,oa-api-key) result)) + ;; Add other params, filtering out nil values + (dolist (param params) + (when (cdr param) + (push param result))) + (nreverse result))) + + (defun oa-get (data query &optional iterable) "Get fields from DATA with QUERY. QUERY is a dot notation string. @@ -77,7 +93,7 @@ Assumes data is in plist form." data) ;; query[] means get query then turn iteration on - ((and (s-ends-with-p "[]" current-field) (null iterable)) + ((and (string-suffix-p "[]" current-field) (null iterable)) (setq current-field (substring current-field 0 -2)) (oa-get (plist-get data (intern-soft (concat ":" current-field))) (when fields @@ -86,7 +102,7 @@ Assumes data is in plist form." ;; this means another level of iteration. You already have a collection. we ;; have to iterate over each one I think. - ((and (s-ends-with-p "[]" current-field) iterable) + ((and (string-suffix-p "[]" current-field) iterable) (setq current-field (substring current-field 0 -2)) (cl-loop for item in data collect (oa-get (plist-get item (intern-soft (concat ":" current-field))) @@ -113,17 +129,16 @@ Assumes data is in plist form." (defun oa--query-all-data (endpoint &rest filter) (let* ((page 1) (url (concat "https://api.openalex.org/" endpoint)) - (filter-string (string-join (cl-loop for key in (plist-get-keys filter) collect - (concat (substring (symbol-name key) 1) - ":" - (url-hexify-string - (plist-get filter key)))) - ",")) - (params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("filter" . ,filter-string) - ("page" . ,page))) - + (filter-string (string-join + (cl-loop for key in (plist-get-keys filter) collect + (concat (substring (symbol-name key) 1) + ":" + (url-hexify-string + (plist-get filter key)))) + ",")) + (params (oa--params `("filter" . ,filter-string) + `("page" . ,page))) + (req (request url :sync t :parser 'oa--response-parser :params params)) (data (request-response-data req)) (meta (plist-get data :meta)) @@ -133,7 +148,7 @@ Assumes data is in plist form." (results (plist-get data :results))) (cl-loop for i from 2 to pages do (setf (cdr (assoc "page" params)) i) - (setq req (request purl :sync t :parser 'oa--response-parser :params params) + (setq req (request url :sync t :parser 'oa--response-parser :params params) data (request-response-data req) results (append results (plist-get data :results)))) results)) @@ -171,12 +186,10 @@ non-nil, and `oa-api-key' if it is non-nil to the API url." (url-hexify-string (plist-get filter key)))) ",")) - + (req (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("filter" . ,filter-string) - ("page" . ,page)))) + :params (oa--params `("filter" . ,filter-string) + `("page" . ,page)))) (data (request-response-data req)) (meta (plist-get data :meta)) (count (plist-get meta :count)) @@ -184,7 +197,7 @@ non-nil, and `oa-api-key' if it is non-nil to the API url." (pages (ceiling (/ (float count) per-page))) (results (plist-get data :results)) (next-page (format "[[elisp:(oa-query \"%s\" %s :page \"%s\")][Next page: %s]]" - endpoint + endpoint (string-join (cl-loop for x in filter collect (if (keywordp x) @@ -194,7 +207,7 @@ non-nil, and `oa-api-key' if it is non-nil to the API url." (+ page 1) (+ page 1))) (buf (generate-new-buffer "*OpenAlex - Query*"))) - + (with-current-buffer buf (erase-buffer) (org-mode) @@ -216,7 +229,7 @@ non-nil, and `oa-api-key' if it is non-nil to the API url." ((string= endpoint "works") (string-join (cl-loop for wrk in results collect - (s-format "*** ${title} + (org-ref--format-template "*** ${title} :PROPERTIES: :HOST: ${primary_location.source.display_name} :YEAR: ${publication_year} @@ -237,8 +250,6 @@ ${get-bibtex} ${abstract} " - (lambda (key data) - (or (cdr (assoc key data)) "")) `(("title" . ,(oa--title wrk)) ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) ("publication_year" . ,(oa-get wrk "publication_year")) @@ -283,9 +294,7 @@ https://docs.openalex.org/api-entities/works" (t (format "/%s" entity-id))))) (req (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("filter" . ,filter)))) + :params (oa--params `("filter" . ,filter)))) (data (request-response-data req))) ;; this is for convenience to inspect data in a browser, e.g. you can click ;; on the url in Emacs and it opens in a browser. @@ -301,9 +310,10 @@ https://docs.openalex.org/api-entities/works" (ivy-more-chars) (let* ((url "https://api.openalex.org/autocomplete/works") (req (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("q" . ,query)))) + :params (let ((params `(("q" . ,query)))) + (when user-mail-address + (push `("mailto" . ,user-mail-address) params)) + (nreverse params)))) (data (request-response-data req)) (results (plist-get data :results))) (cl-loop for work in results collect @@ -318,11 +328,11 @@ https://docs.openalex.org/api-entities/works" "Autocomplete works. This doesn't seem as useful as it could be." (interactive) - + (ivy-read "Work: " #'oa--works-candidates :dynamic-collection t :action - '(1 + '(1 ("o" (lambda (candidate) (browse-url (get-text-property 0 'oaid candidate))) "Open in browser")))) @@ -338,7 +348,7 @@ This doesn't seem as useful as it could be." (defun oa--authors (wrk) "Return an author string for WRK. The string is a comma-separated list of links to author pages in OpenAlex." - (s-join ", " (cl-loop for author in (plist-get wrk :authorships) + (string-join (cl-loop for author in (plist-get wrk :authorships) collect (format "[[elisp:(oa--author-org \"%s\")][%s]]" (plist-get @@ -346,7 +356,8 @@ The string is a comma-separated list of links to author pages in OpenAlex." :id) (plist-get (plist-get author :author) - :display_name))))) + :display_name))) + ", ")) (defun oa--title (wrk) @@ -358,7 +369,7 @@ The string is a comma-separated list of links to author pages in OpenAlex." ;; be nice to integrate M-, navigation. (defun oa--elisp-get-bibtex (wrk) "Return a elisp link to get a bibtex entry for WRK if there is a doi." - (if-let ((doi (plist-get wrk :doi))) + (if-let* ((doi (plist-get wrk :doi))) (format "[[elisp:(doi-add-bibtex-entry \"%s\")][Get bibtex entry]]" doi) "")) @@ -389,7 +400,7 @@ The string is a comma-separated list of links to author pages in OpenAlex." WORKS is a list of results from OpenAlex." (cl-loop for wrk in (plist-get works :results) collect - (s-format "** ${title} + (org-ref--format-template "** ${title} :PROPERTIES: :HOST: ${primary_location.source.display_name} :YEAR: ${publication_year} @@ -407,8 +418,6 @@ ${get-bibtex} - ${oa-cited} " - (lambda (key data) - (or (cdr (assoc key data)) "")) `(("title" . ,(oa-get wrk "title")) ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) ("publication_year" . ,(oa-get wrk "publication_year")) @@ -428,7 +437,7 @@ HEADER is the first thing in the buffer WORKS is usually a list of results from OpenAlex. Argument ENTRIES A list of strings for each org entry." (let ((buf (get-buffer-create bufname))) - + (with-current-buffer buf (erase-buffer) (insert header) @@ -441,7 +450,7 @@ elisp:org-columns elisp:org-columns-quit | cited by | [[elisp:(oa-buffer-sort-cited-by-count t)][low first]] | [[elisp:(oa-buffer-sort-cited-by-count)][high first]] | ") - (insert (s-join "\n" entries)) + (insert (string-join entries "\n")) (org-mode) (goto-char (point-min)) (org-next-visible-heading 1)) @@ -456,21 +465,21 @@ elisp:org-columns elisp:org-columns-quit split entries) (while related-work - (setq split (-split-at 25 related-work) + (setq split (org-ref--split-at 25 related-work) related-work (nth 1 split)) - + ;; split is what we process now (setq entries (append entries (oa--works-entries - (oa--work (format "?filter=openalex:%s" (s-join "|" (nth 0 split)))))))) - + (oa--work (format "?filter=openalex:%s" (string-join (nth 0 split) "|"))))))) + (oa--works-buffer "*OpenAlex - Related works*" (format "* OpenAlex - Related works for %s ([[%s][json]]) %s\n\n" entity-id (plist-get wrk :oa-url) - (s-format ":PROPERTIES: + (org-ref--format-template ":PROPERTIES: :TITLE: ${title} :HOST: ${primary_location.source.display_name} :AUTHOR: ${authors} @@ -481,15 +490,13 @@ elisp:org-columns elisp:org-columns-quit Found ${nentries} results. " - (lambda (key data) - (or (cdr (assoc key data)) "")) - `(("title" . ,(oa-get wrk "title")) - ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) - ("authors" . ,(oa--authors wrk)) - ("doi" . ,(oa-get wrk "doi")) - ("publication_year" . ,(oa-get wrk "publication_year")) - ("id" . ,(oa-get wrk "id")) - ("nentries" . ,(length entries))))) + `(("title" . ,(oa-get wrk "title")) + ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) + ("authors" . ,(oa--authors wrk)) + ("doi" . ,(oa-get wrk "doi")) + ("publication_year" . ,(oa-get wrk "publication_year")) + ("id" . ,(oa-get wrk "id")) + ("nentries" . ,(length entries))))) entries))) @@ -500,20 +507,20 @@ Found ${nentries} results. split (entries '())) (while referenced-work - (setq split (-split-at 25 referenced-work) + (setq split (org-ref--split-at 25 referenced-work) referenced-work (nth 1 split)) ;; split is what we process now (setq entries (append entries (oa--works-entries - (oa--work (format "?filter=openalex:%s" - (s-join "|" (nth 0 split)))))))) + (oa--work (format "?filter=openalex:%s" + (string-join (nth 0 split) "|"))))))) (oa--works-buffer "*OpenAlex - References*" (format "* OpenAlex - References from %s ([[%s][json]]) %s\n\n" entity-id (plist-get wrk :oa-url) - (s-format ":PROPERTIES: + (org-ref--format-template ":PROPERTIES: :TITLE: ${title} :HOST: ${primary_location.source.display_name} :AUTHOR: ${authors} @@ -524,15 +531,13 @@ Found ${nentries} results. Found ${nentries} results. " - (lambda (key data) - (or (cdr (assoc key data)) "")) - `(("title" . ,(oa-get wrk "title")) - ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) - ("authors" . ,(oa--authors wrk)) - ("doi" . ,(oa-get wrk "doi")) - ("publication_year" . ,(oa-get wrk "publication_year")) - ("id" . ,(oa-get wrk "id")) - ("nentries" . ,(length entries))))) + `(("title" . ,(oa-get wrk "title")) + ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) + ("authors" . ,(oa--authors wrk)) + ("doi" . ,(oa-get wrk "doi")) + ("publication_year" . ,(oa-get wrk "publication_year")) + ("id" . ,(oa-get wrk "id")) + ("nentries" . ,(length entries))))) entries))) @@ -546,8 +551,7 @@ Found ${nentries} results. (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key))))) + :params (oa--params)))) (count (plist-get (plist-get cited-by-works :meta) :count)) (per-page (plist-get (plist-get cited-by-works :meta) :per_page)) (entries '()) @@ -559,19 +563,17 @@ Found ${nentries} results. (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("page" . ,page))))) + :params (oa--params `("page" . ,page))))) (setq entries (append entries (oa--works-entries cited-by-works))) (cl-incf page)) - + (oa--works-buffer "*OpenAlex - Cited by*" (format "* OpenAlex - %s Cited by ([[%s][json]]) %s" entity-id url - (s-format ":PROPERTIES: + (org-ref--format-template ":PROPERTIES: :TITLE: ${title} :HOST: ${primary_location.source.display_name} :AUTHOR: ${authors} @@ -583,8 +585,6 @@ Found ${nentries} results. Found ${nentries} results. " - (lambda (key data) - (or (cdr (assoc key data)) "")) `(("title" . ,(oa-get wrk "title")) ("primary_location.source.display_name" . ,(oa-get wrk "primary_location.source.display_name")) ("authors" . ,(oa--authors wrk)) @@ -665,12 +665,15 @@ With prefix arg ASCENDING sort from low to high." (browse-url (plist-get data :id)))) -(defhydra+ org-ref-citation-hydra () - "Add open from action to `org-ref-citation-hydra'." - ("xa" oa-open "Open in OpenAlex" :column "OpenAlex") - ("xr" oa-related-works "Related documents" :column "OpenAlex") - ("xc" oa-cited-by-works "Cited by documents" :column "OpenAlex") - ("xf" oa-referenced-works "References from" :column "OpenAlex")) +(with-eval-after-load 'org-ref-citation-links + (transient-append-suffix 'org-ref-citation-menu "u" + '("xa" "Open in OpenAlex" oa-open)) + (transient-append-suffix 'org-ref-citation-menu "xa" + '("xr" "Related documents" oa-related-works)) + (transient-append-suffix 'org-ref-citation-menu "xr" + '("xc" "Cited by documents" oa-cited-by-works)) + (transient-append-suffix 'org-ref-citation-menu "xc" + '("xf" "References from" oa-referenced-works))) ;; * Author object @@ -681,10 +684,8 @@ FILTER is an optional string to add to the URL." (let* ((url (concat "https://api.openalex.org/authors" entity-id)) (req (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("filter" . ,filter)))) - (data (request-response-data req))) + :params (oa--params `("filter" . ,filter)))) + (data (request-response-data req))) ;; this is for convenience to inspect data in a browser. (plist-put data :oa-url url) data)) @@ -692,15 +693,14 @@ FILTER is an optional string to add to the URL." (defun oa--author-entries (works-data url) "Get entries from WORKS-DATA." - (let* ((meta (plist-get works-data :meta)) + (let* ((meta (plist-get works-data :meta)) (per-page (plist-get meta :per_page)) (count (plist-get meta :count)) (pages (/ count per-page)) - (entries '()) - purl) + (entries '())) ;; if there is a remainder we need to get the rest (when (> (mod count per-page) 0) (cl-incf pages)) - + ;; Now we have to loop through the pages (cl-loop for i from 1 to pages do @@ -708,13 +708,11 @@ FILTER is an optional string to add to the URL." (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("page" . ,i)))) + :params (oa--params `("page" . ,i)))) entries (append entries (cl-loop for result in (plist-get works-data :results) collect - (s-format "*** ${title} + (org-ref--format-template "*** ${title} :PROPERTIES: :ID: ${id} :DOI: ${ids.doi} @@ -734,9 +732,7 @@ ${get-bibtex} ${abstract} - " (lambda (key data) - (or (cdr (assoc key data)) "")) - `(("title" . ,(oa--title result)) + " `(("title" . ,(oa--title result)) ("id" . ,(oa-get result "id")) ("ids.doi" . ,(oa-get result "ids.doi")) ("publication_year" . ,(oa-get result "publication_year")) @@ -787,9 +783,10 @@ ${abstract} (ivy-more-chars) (let* ((url "https://api.openalex.org/autocomplete/authors") (req (request url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("q" . ,query)))) + :params (let ((params `(("q" . ,query)))) + (when user-mail-address + (push `("mailto" . ,user-mail-address) params)) + (nreverse params)))) (data (request-response-data req)) (results (plist-get data :results))) (cl-loop for author in results collect @@ -801,6 +798,20 @@ ${abstract} 'oaid (plist-get author :id)))))) +(defun oa--format-institution (data) + "Format institution from DATA, handling nil values gracefully. +Extracts the first institution from last_known_institutions array." + (let* ((institutions (plist-get data :last_known_institutions)) + (first-inst (and institutions (listp institutions) (car institutions))) + (name (and first-inst (plist-get first-inst :display_name))) + (country (and first-inst (plist-get first-inst :country_code)))) + (cond + ((and name country) (format "%s, %s" name country)) + (name name) + (country country) + (t "")))) + + (defun oa--counts-by-year (data) "Get citation counts by year and make a graph. DATA is an author from OpenAlex. @@ -858,18 +869,17 @@ plot $counts using 1:3:xtic(2) with boxes lc rgb \"grey\" title \"Citations per (request works-url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key)))))) + :params (oa--params))))) (with-current-buffer buf (erase-buffer) - (insert (s-format "* ${display_name} ([[${oa-url}][json]]) + (insert (org-ref--format-template "* ${display_name} ([[${oa-url}][json]]) :PROPERTIES: :OPENALEX: ${id} :ORCID: ${orcid} :SCOPUS: ${ids.scopus} :WORKS_COUNT: ${works_count} :CITED_BY_COUNT: ${cited_by_count} -:INSTITUTION: ${last_known_institution.display_name}, ${last_known_institution.country_code} +:INSTITUTION: ${institution} :END: #+COLUMNS: %25ITEM %YEAR %CITED_BY_COUNT @@ -884,18 +894,16 @@ ${citations-image} | cited by | [[elisp:(oa-buffer-sort-cited-by-count t)][low first]] | [[elisp:(oa-buffer-sort-cited-by-count)][high first]] | " - (lambda (key data) - (or (cdr (assoc key data)) "")) `(("display_name" . ,(oa-get data "display_name")) ("oa-url" . ,(oa-get data "oa-url")) ("id" . ,(oa-get data "id")) ("orcid" . ,(oa-get data "orcid")) ("ids.scopus" . ,(oa-get data "ids.scopus")) ("works_count" . ,(oa-get data "works_count")) - ("last_known_institution.display_name" . ,(oa-get data "last_known_institution.display_name")) - ("last_known_institution.country_code" . ,(oa-get data "last_known_institution.country_code")) + ("cited_by_count" . ,(oa-get data "cited_by_count")) + ("institution" . ,(oa--format-institution data)) ("citations-image" . ,citations-image)))) - (insert (s-join "\n" (oa--author-entries works-data works-url))) + (insert (string-join (oa--author-entries works-data works-url) "\n")) (org-mode) (goto-char (point-min)) @@ -906,7 +914,7 @@ ${citations-image} (defun oa-author () "Get data and act on it for an author." (interactive) - + (ivy-read "Author: " #'oa--author-candidates :dynamic-collection t :action @@ -936,10 +944,8 @@ PAGE is optional, and loads that page of results. Defaults to 1." (req (request url :sync t :parser #'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("page" . ,page) - ("filter" . ,(format "fulltext.search:%s" query))))) + :params (oa--params `("page" . ,page) + `("filter" . ,(format "fulltext.search:%s" query))))) (data (request-response-data req)) (metadata (plist-get data :meta)) (count (plist-get metadata :count)) @@ -952,29 +958,27 @@ PAGE is optional, and loads that page of results. Defaults to 1." (+ page 1) npages)) (buf (get-buffer-create "*OpenAlex Full-text search*"))) - + (with-current-buffer buf (erase-buffer) (org-mode) - (insert (s-format "#+title: Full-text search: ${query} + (insert (org-ref--format-template "#+title: Full-text search: ${query} [[elisp:(oa-fulltext-search \"${query}\" ${page})]]" - 'aget `(("query" . ,query) ("page" . ,page)))) - (insert (s-format + (insert (org-ref--format-template " ${meta.count} results: Page ${meta.page} of ${s1} ${s2} \n\n" - (lambda (key data) - (or (cdr (assoc key data)) "")) - `(("meta.page" . ,(oa-get data "meta.page")) + `(("meta.count" . ,count) + ("meta.page" . ,(oa-get data "meta.page")) ("s1" . ,(format "%s" npages)) ("s2" . ,(format "%s" next-page))))) - + (insert - (cl-loop for result in results concat - (s-format "* ${title} + (cl-loop for result in results concat + (org-ref--format-template "* ${title} :PROPERTIES: :JOURNAL: ${primary_location.source.display_name} :AUTHOR: ${authors} @@ -989,10 +993,7 @@ ${get-bibtex} - ${oa-related} - ${oa-cited} -" (lambda (key data) - (or (cdr (assoc key data)) "")) - -`(("title" . ,(oa--title result)) +" `(("title" . ,(oa--title result)) ("primary_location.source.display_name" . ,(oa-get result "primary_location.source.display_name")) ("authors" . ,(oa--authors result)) ("publication_year" . ,(oa-get result "publication_year")) @@ -1004,7 +1005,7 @@ ${get-bibtex} ("oa-cited" . ,(oa--elisp-get-oa-cited-by result)))))) (insert next-page) - + (goto-char (point-min))) (pop-to-buffer buf))) @@ -1050,23 +1051,21 @@ Recently published papers are probably missing. :dynamic-collection t)) (when (y-or-n-p "Save to file?") (read-file-name "File: ")))) - + (let* ((data (oa--author entity-id)) - (works-url (plist-get data :works_api_url)) + (works-url (plist-get data :works_api_url)) (works-data (request-response-data (request works-url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key))))) + :params (oa--params)))) (meta (plist-get works-data :meta)) (count (plist-get meta :count)) (per-page (plist-get meta :per_page)) (pages (/ count per-page)) (results (plist-get works-data :results)) (current-year (string-to-number (format-time-string "%Y" (current-time)))) - (current-authors '()) - purl) + (current-authors '())) ;; Now we need to accumulate the rest of the results from other pages (when (> (mod count per-page) 0) (cl-incf pages)) @@ -1077,11 +1076,9 @@ Recently published papers are probably missing. (request works-url :sync t :parser 'oa--response-parser - :params `(("mailto" . ,user-mail-address) - ("api_key" . ,oa-api-key) - ("page" . ,i)))) + :params (oa--params `("page" . ,i)))) results (append results (plist-get works-data :results)))) - + ;; Now results is a list of your publications. We need to iterate over each ;; one, and accumulate author information (cl-loop for result in results do @@ -1093,30 +1090,30 @@ Recently published papers are probably missing. (name-parts (mapcar #'capitalize (split-string name))) (name-string (concat (car (last name-parts)) ", " (string-join (butlast name-parts) " "))) - - (institutions (plist-get authorship :institutions)) + + (institutions (plist-get authorship :institutions)) (institution (plist-get (car institutions) :display_name))) ;; name, institution, contact info, last-active ;; we won't have contact info from openalex. (push (list name-string institution "" year (plist-get result :publication_date)) current-authors)))))) - + (setq current-authors (sort current-authors (lambda (a b) "Sort first on name, then on year in descending order" (if (string= (nth 0 a) (nth 0 b)) (> (nth 3 a) (nth 3 b)) (string< (car a) (car b)))))) - + ;; now filter for unique authors (setq current-authors (cl-loop for group in (seq-group-by (lambda (x) (car x)) - current-authors) + current-authors) collect (cadr group))) ;; Finally lets fix the year so Excel reads it correctly. I use the publication date - (setq current-authors (cl-loop for row in current-authors + (setq current-authors (cl-loop for row in current-authors collect (list "A:" (nth 0 row) @@ -1132,7 +1129,7 @@ Recently published papers are probably missing. row) "\t") "\n"))) - + (kill-new (mapconcat (lambda (row) (concat (string-join (mapcar (lambda (x) @@ -1152,7 +1149,7 @@ Recently published papers are probably missing. (interactive) (cl-loop for buf in (buffer-list) do - (when (s-starts-with? "*OpenAlex" (buffer-name buf)) + (when (string-prefix-p "*OpenAlex" (buffer-name buf)) (kill-buffer buf)))) @@ -1165,7 +1162,7 @@ Operates on headings with a DOI property." (lambda () (kill-new (org-entry-get (point) "DOI")) (doi-utils-add-bibtex-entry-from-doi - (doi-utils-maybe-doi-from-region-or-current-kill) + (doi-utils-maybe-doi-from-region-or-current-kill) bibfile)) "DOI<>\"\""))) diff --git a/elpa/org-ref-20241124.2012/org-ref-arxiv.el b/elpa/org-ref-20251206.1422/org-ref-arxiv.el similarity index 89% rename from elpa/org-ref-20241124.2012/org-ref-arxiv.el rename to elpa/org-ref-20251206.1422/org-ref-arxiv.el index c16739c7..f2c26fe8 100644 --- a/elpa/org-ref-20241124.2012/org-ref-arxiv.el +++ b/elpa/org-ref-20251206.1422/org-ref-arxiv.el @@ -26,10 +26,7 @@ ;;; Code: (require 'bibtex) -(require 'dash) -(require 'f) (require 'org) -(require 's) (require 'org-ref-utils) (require 'parsebib) @@ -42,6 +39,7 @@ (declare-function parsebib-find-bibtex-dialect "parsebib") (declare-function org-ref-clean-bibtex-entry "org-ref-core") +(declare-function org-ref-normalize-bibtex-completion-bibliography "org-ref-utils") ;; this is a C function (declare-function libxml-parse-xml-region "xml") @@ -85,7 +83,7 @@ (with-current-buffer (url-retrieve-synchronously (format "https://ui.adsabs.harvard.edu/abs/%s/exportcitation" arxiv-bibliographic-code)) (when (re-search-forward - "\\(.*\\(?:\n.*\\)*?\\(?:\n\\s-*\n\\|\\'\\)\\)" + "\\(.*\\(?:\n.*\\)*?\\)\\(?:\n\\s-*\n\\)" nil t) (xml-substitute-special (match-string 1))))) @@ -121,13 +119,13 @@ Returns a formatted BibTeX entry." (match-beginning 0)) (point-max))) (entry (assq 'entry parse-tree)) - (authors (--map (nth 2 (nth 2 it)) - (--filter (and (listp it) (eq (car it) 'author)) entry))) + (authors (mapcar (lambda (it) (nth 2 (nth 2 it))) + (seq-filter (lambda (it) (and (listp it) (eq (car it) 'author))) entry))) (year (format-time-string "%Y" (date-to-time (nth 2 (assq 'published entry))))) (title (nth 2 (assq 'title entry))) (names (arxiv-bibtexify-authors authors)) (category (cdar (nth 1 (assq 'primary_category entry)))) - (abstract (s-trim (nth 2 (assq 'summary entry)))) + (abstract (string-trim (nth 2 (assq 'summary entry)))) (url (nth 2 (assq 'id entry))) (temp-bibtex (format arxiv-entry-format-string "" title names year arxiv-number category abstract url)) (key (with-temp-buffer @@ -145,9 +143,12 @@ Returns a formatted BibTeX entry." (defun arxiv-bibtexify-authors (authors) "Return names in 'SURNAME, FIRST NAME' format from AUTHORS list." - (s-join " and " - (--map (concat (-last-item it) ", " (s-join " " (-remove-last 'stringp it))) - (--map (s-split " +" it) authors)))) + (string-join + (mapcar (lambda (it) + (concat (car (last it)) ", " + (string-join (butlast it) " "))) + (mapcar (lambda (it) (split-string it " +")) authors)) + " and ")) (defun arxiv-maybe-arxiv-id-from-current-kill () @@ -166,22 +167,22 @@ Returns a formatted BibTeX entry." (;; check if current-kill looks like an arxiv ID ;; if so, return it ;; Ex: 1304.4404v2 - (s-match (concat "^" arxiv-id-regexp) the-current-kill) + (org-ref--string-match (concat "^" arxiv-id-regexp) the-current-kill) the-current-kill) (;; check if current-kill looks like an arxiv cite ;; if so, remove the prefix and return ;; Ex: arXiv:1304.4404v2 --> 1304.4404v2 - (s-match (concat arxiv-cite-prefix-regexp arxiv-id-regexp "$") the-current-kill) + (org-ref--string-match (concat arxiv-cite-prefix-regexp arxiv-id-regexp "$") the-current-kill) (replace-regexp-in-string arxiv-cite-prefix-regexp "" the-current-kill)) (;; check if current-kill looks like an arxiv url ;; if so, remove the url prefix and return ;; Ex: https://arxiv.org/abs/1304.4404 --> 1304.4404 - (s-match (concat arxiv-url-prefix-regexp arxiv-id-regexp "$") the-current-kill) + (org-ref--string-match (concat arxiv-url-prefix-regexp arxiv-id-regexp "$") the-current-kill) (replace-regexp-in-string arxiv-url-prefix-regexp "" the-current-kill)) (;; check if current-kill looks like an arxiv PDF url ;; if so, remove the url prefix, the .pdf suffix, and return ;; Ex: https://arxiv.org/pdf/1304.4404.pdf --> 1304.4404 - (s-match (concat arxiv-url-prefix-regexp arxiv-id-regexp "\\.pdf$") the-current-kill) + (org-ref--string-match (concat arxiv-url-prefix-regexp arxiv-id-regexp "\\.pdf$") the-current-kill) (replace-regexp-in-string arxiv-url-prefix-regexp "" (substring the-current-kill 0 (- (length the-current-kill) 4)))) ;; otherwise, return nil (t @@ -199,10 +200,8 @@ Returns a formatted BibTeX entry." ;; now get the bibfile to add it to (completing-read "Bibfile: " - (append (f-entries "." (lambda (f) (f-ext? f "bib"))) - (if (stringp bibtex-completion-bibliography) - (list bibtex-completion-bibliography) - bibtex-completion-bibliography))))) + (append (org-ref--directory-files "." (lambda (f) (org-ref--file-ext-p f "bib"))) + (org-ref-normalize-bibtex-completion-bibliography))))) (save-window-excursion (find-file bibfile) (goto-char (point-max)) @@ -245,8 +244,8 @@ key." ;; now get the bibfile to add it to (completing-read "Bibfile: " - (append (f-entries "." (lambda (f) (f-ext? f "bib"))) - bibtex-completion-bibliography)) + (append (org-ref--directory-files "." (lambda (f) (org-ref--file-ext-p f "bib"))) + (org-ref-normalize-bibtex-completion-bibliography))) (cond ((stringp bibtex-completion-library-path) bibtex-completion-library-path) @@ -288,7 +287,8 @@ key." (when (file-exists-p (concat pdfdir key ".pdf")) (bibtex-end-of-entry) (backward-char) - (insert (format " file = {%s}\n " (concat pdfdir key ".pdf"))))))) + (insert (format " file = {%s}\n " (concat pdfdir key ".pdf"))))) + (save-buffer))) (provide 'org-ref-arxiv) diff --git a/elpa/org-ref-20241124.2012/org-ref-autoloads.el b/elpa/org-ref-20251206.1422/org-ref-autoloads.el similarity index 99% rename from elpa/org-ref-20241124.2012/org-ref-autoloads.el rename to elpa/org-ref-20251206.1422/org-ref-autoloads.el index 912667b0..d6f72b48 100644 --- a/elpa/org-ref-20241124.2012/org-ref-autoloads.el +++ b/elpa/org-ref-20251206.1422/org-ref-autoloads.el @@ -130,7 +130,7 @@ Search NIST webbook for NAME. ;;; Generated autoloads from openalex.el -(register-definition-prefixes "openalex" '("oa-" "org-ref-citation-hydra")) +(register-definition-prefixes "openalex" '("oa-")) ;;; Generated autoloads from org-ref-arxiv.el @@ -527,7 +527,7 @@ Open QUERY as a basic title-abstract-keyword search at scopus.com. Open QUERY as an advanced search at scopus.com. (fn QUERY)" t) -(register-definition-prefixes "org-ref-scopus" '("*hydra-eid*" "*scopus-api-key*" "scopus")) +(register-definition-prefixes "org-ref-scopus" '("*scopus-api-key*" "org-ref-scopus--current-eid" "scopus")) ;;; Generated autoloads from org-ref-url-utils.el diff --git a/elpa/org-ref-20241124.2012/org-ref-bibliography-links.el b/elpa/org-ref-20251206.1422/org-ref-bibliography-links.el similarity index 98% rename from elpa/org-ref-20241124.2012/org-ref-bibliography-links.el rename to elpa/org-ref-20251206.1422/org-ref-bibliography-links.el index 6deb0b1b..f5c996bf 100644 --- a/elpa/org-ref-20241124.2012/org-ref-bibliography-links.el +++ b/elpa/org-ref-20251206.1422/org-ref-bibliography-links.el @@ -257,11 +257,11 @@ Returns `org-ref-bst-styles' or sets it and returns it." (setq org-ref-bst-styles (mapcar 'file-name-nondirectory (mapcar 'file-name-sans-extension - (-flatten + (org-ref--flatten-list (mapcar (lambda (path) (setq path (replace-regexp-in-string "!" "" path)) (when (file-directory-p path) - (f-entries path (lambda (f) (f-ext? f "bst"))))) + (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'") diff --git a/elpa/org-ref-20241124.2012/org-ref-bibtex.el b/elpa/org-ref-20251206.1422/org-ref-bibtex.el similarity index 86% rename from elpa/org-ref-20241124.2012/org-ref-bibtex.el rename to elpa/org-ref-20251206.1422/org-ref-bibtex.el index 3c666836..b45399c9 100644 --- a/elpa/org-ref-20241124.2012/org-ref-bibtex.el +++ b/elpa/org-ref-20251206.1422/org-ref-bibtex.el @@ -52,18 +52,16 @@ ;; org-ref-replace-nonascii :: replace nonascii characters in a bibtex ;; entry. Replacements are in `org-ref-nonascii-latex-replacements'. ;; -;; ** hydra menu for bibtex files -;; `org-ref-bibtex-hydra/body' gives a hydra menu to a lot of useful functions. -;; `org-ref-bibtex-new-entry/body' gives a hydra menu to add new bibtex entries. -;; `org-ref-bibtex-file/body' gives a hydra menu of actions for the bibtex file +;; ** Transient menu for bibtex files +;; `org-ref-bibtex-entry-menu' gives access to many useful functions. +;; `org-ref-bibtex-new-entry-menu' presents commands to add new bibtex entries. +;; `org-ref-bibtex-file-menu' collects actions for entire bibtex files. ;; ;;; Code (require 'bibtex) -(require 'dash) -(require 'hydra) +(require 'transient) (require 'message) -(require 's) (require 'doi-utils) (require 'avy) (require 'sgml-mode) @@ -419,7 +417,7 @@ START and END allow you to use this with `bibtex-map-entries'" (lambda (row) (cons (nth 2 row) (nth 0 row))) org-ref-bibtex-journal-abbreviations)) - (journal (s-trim (bibtex-autokey-get-field "journal"))) + (journal (string-trim (bibtex-autokey-get-field "journal"))) (bstring (or (cdr (assoc journal full-names)) (cdr (assoc journal abbrev-names))))) @@ -512,19 +510,18 @@ books." word) ;; these words should not be capitalized, unless they ;; are the first word - ((-contains? org-ref-lower-case-words - (s-downcase word)) - (s-downcase word)) + ((member (downcase word) org-ref-lower-case-words) + (downcase word)) ;; Words that are quoted - ((s-starts-with? "\"" word) - (concat "\"" (s-capitalize (substring word 1)))) + ((string-prefix-p "\"" word) + (concat "\"" (capitalize (substring word 1)))) (t - (s-capitalize word)))) + (capitalize word)))) words)) ;; Check if first word should be capitalized - (when (-contains? org-ref-lower-case-words (car words)) - (setf (car words) (s-capitalize (car words)))) + (when (member (car words) org-ref-lower-case-words) + (setf (car words) (capitalize (car words)))) (setq title (mapconcat 'identity words " ")) @@ -578,11 +575,11 @@ all the title entries in articles." ;; LaTeX or protected words (string-match "\\$\\|{\\|}\\|\\\\" word) word - (s-downcase word))) + (downcase word))) words)) ;; capitalize first word - (setf (car words) (s-capitalize (car words))) + (setf (car words) (capitalize (car words))) ;; join the words (setq title (mapconcat 'identity words " ")) @@ -816,132 +813,137 @@ a directory. Optional PREFIX argument toggles between ;;* Hydra menus ;;** Hydra menu for bibtex entries -;; hydra menu for actions on bibtex entries -(defhydra org-ref-bibtex-hydra (:color blue :hint nil) - "Bibtex actions: -" - ;; Open-like actions - ("p" org-ref-open-bibtex-pdf "PDF" :column "Open") - ("n" org-ref-open-bibtex-notes "Notes" :column "Open") - ("b" org-ref-open-in-browser "URL" :column "Open") - - ;; edit/modify - ("K" (lambda () - (interactive) - (org-ref-set-bibtex-keywords - (read-string "Keywords: " - (bibtex-autokey-get-field "keywords")) - t)) - "Keywords" :column "Edit") - ("a" org-ref-replace-nonascii "Replace nonascii" :column "Edit") - ("s" org-ref-sort-bibtex-entry "Sort fields" :column "Edit") - ("T" org-ref-title-case-article "Title case" :column "Edit") - ("S" org-ref-sentence-case-article "Sentence case" :column "Edit") - ("U" (doi-utils-update-bibtex-entry-from-doi (org-ref-bibtex-entry-doi)) "Update entry" :column "Edit") - ("u" doi-utils-update-field "Update field" :column "Edit" :color red) - ("" (cl--set-buffer-substring (line-beginning-position) (+ 1 (line-end-position)) "") - "Delete line" :column "Edit" :color red) - ("d" bibtex-kill-entry "Kill entry" :column "Edit") - ("L" org-ref-clean-bibtex-entry "Clean entry" :column "Edit") - ("A" org-ref-bibtex-assoc-pdf-with-entry "Add pdf" :column "Edit") - ("r" (lambda () - (interactive) - (bibtex-beginning-of-entry) - (bibtex-kill-entry) - (find-file (completing-read - "Bibtex file: " - (append bibtex-completion-bibliography - (f-entries "." (lambda (f) (f-ext? f "bib")))))) - (goto-char (point-max)) - (bibtex-yank) - (save-buffer) - (kill-buffer)) - "Refile entry" :column "Edit") - - ;; www - ("P" org-ref-bibtex-pubmed "Pubmed" :column "WWW") - ("w" org-ref-bibtex-wos "WOS" :column "WWW") - ("c" org-ref-bibtex-wos-citing "WOS citing" :column "WWW") - ("a" org-ref-bibtex-wos-related "WOS related" :column "WWW") - ("R" org-ref-bibtex-crossref "Crossref" :column "WWW") - ("g" org-ref-bibtex-google-scholar "Google Scholar" :column "WWW") - ("e" org-ref-email-bibtex-entry "Email" :column "WWW") - - - ;; Copy - ("o" (lambda () - (interactive) - (bibtex-copy-entry-as-kill) - (message "Use %s to paste the entry" - (substitute-command-keys (format "\\[bibtex-yank]")))) - "Copy entry" :column "Copy") - - ("y" (save-excursion - (bibtex-beginning-of-entry) - (when (looking-at bibtex-entry-maybe-empty-head) - (kill-new (bibtex-key-in-head)))) - "Copy key" :column "Copy") - - ("f" (save-excursion - (bibtex-beginning-of-entry) - (kill-new (bibtex-completion-apa-format-reference - (cdr (assoc "=key=" (bibtex-parse-entry t)))))) - "Formatted entry" :column "Copy") - - ;; Navigation - ("[" org-ref-bibtex-next-entry "Next entry" :column "Navigation" :color red) - ("]" org-ref-bibtex-previous-entry "Previous entry" :column "Navigation" :color red) - ("" next-line "Next line" :column "Navigation" :color red) - ("" previous-line "Previous line" :column "Navigation" :color red) - ("" scroll-up-command "Scroll up" :column "Navigation" :color red) - ("" scroll-down-command "Scroll down" :column "Navigation" :color red) - ("v" org-ref-bibtex-visible-entry "Visible entry" :column "Navigation" :color red) - ("V" org-ref-bibtex-visible-field "Visible field" :column "Navigation" :color red) - - - ;; Miscellaneous - ("F" org-ref-bibtex-file/body "File hydra" :column "Misc") - ("N" org-ref-bibtex-new-entry/body "New entry" :column "Misc") - ("q" nil)) +;; transient menu for actions on bibtex entries +(transient-define-prefix org-ref-bibtex-entry-menu () + "Bibtex actions." + [["Open" + ("p" "PDF" org-ref-open-bibtex-pdf) + ("n" "Notes" org-ref-open-bibtex-notes) + ("b" "URL" org-ref-open-in-browser)] + ["Edit" + ("K" "Keywords" (lambda () + (interactive) + (org-ref-set-bibtex-keywords + (read-string "Keywords: " + (bibtex-autokey-get-field "keywords")) + t))) + ("a" "Replace nonascii" org-ref-replace-nonascii) + ("s" "Sort fields" org-ref-sort-bibtex-entry) + ("T" "Title case" org-ref-title-case-article) + ("S" "Sentence case" org-ref-sentence-case-article) + ("U" "Update entry" (lambda () + (interactive) + (doi-utils-update-bibtex-entry-from-doi (org-ref-bibtex-entry-doi)))) + ("u" "Update field" doi-utils-update-field :transient t) + ("" "Delete line" (lambda () + (interactive) + (cl--set-buffer-substring + (line-beginning-position) + (1+ (line-end-position)) + "")) :transient t) + ("d" "Kill entry" bibtex-kill-entry) + ("L" "Clean entry" org-ref-clean-bibtex-entry) + ("A" "Add pdf" org-ref-bibtex-assoc-pdf-with-entry) + ("r" "Refile entry" (lambda () + (interactive) + (bibtex-beginning-of-entry) + (bibtex-kill-entry) + (find-file (completing-read + "Bibtex file: " + (append bibtex-completion-bibliography + (org-ref--directory-files "." (lambda (f) (org-ref--file-ext-p f "bib")))))) + (goto-char (point-max)) + (bibtex-yank) + (save-buffer) + (kill-buffer)))] + ["WWW" + ("P" "Pubmed" org-ref-bibtex-pubmed) + ("w" "WOS" org-ref-bibtex-wos) + ("c" "WOS citing" org-ref-bibtex-wos-citing) + ("a" "WOS related" org-ref-bibtex-wos-related) + ("R" "Crossref" org-ref-bibtex-crossref) + ("g" "Google Scholar" org-ref-bibtex-google-scholar) + ("e" "Email" org-ref-email-bibtex-entry)] + ["Copy" + ("o" "Copy entry" (lambda () + (interactive) + (bibtex-copy-entry-as-kill) + (message "Use %s to paste the entry" + (substitute-command-keys (format "\\[bibtex-yank]"))))) + ("y" "Copy key" (lambda () + (interactive) + (save-excursion + (bibtex-beginning-of-entry) + (when (looking-at bibtex-entry-maybe-empty-head) + (kill-new (bibtex-key-in-head)))))) + ("f" "Formatted entry" (lambda () + (interactive) + (save-excursion + (bibtex-beginning-of-entry) + (kill-new (bibtex-completion-apa-format-reference + (cdr (assoc "=key=" (bibtex-parse-entry t))))))))] + ["Navigation" + ("[" "Next entry" org-ref-bibtex-next-entry :transient t) + ("]" "Previous entry" org-ref-bibtex-previous-entry :transient t) + ("" "Next line" next-line :transient t) + ("" "Previous line" previous-line :transient t) + ("" "Scroll up" scroll-up-command :transient t) + ("" "Scroll down" scroll-down-command :transient t) + ("v" "Visible entry" org-ref-bibtex-visible-entry :transient t) + ("V" "Visible field" org-ref-bibtex-visible-field :transient t)] + ["Misc" + ("F" "File menu" org-ref-bibtex-file-menu) + ("N" "New entry" org-ref-bibtex-new-entry-menu) + ("q" "Quit" transient-quit-one)]]) (declare-function biblio-lookup "biblio") (declare-function arxiv-add-bibtex-entry "org-ref-arxiv") (declare-function doi-insert-bibtex "doi-utils") -;;** Hydra menu for new bibtex entries -;; A hydra for adding new bibtex entries. -(defhydra org-ref-bibtex-new-entry (:color blue) - "New Bibtex entry:" - ("d" doi-insert-bibtex "from DOI" :column "Automatic") - ("c" crossref-add-bibtex-entry "from Crossref" :column "Automatic") - ("a" arxiv-add-bibtex-entry "From Arxiv" :column "Automatic") - ("b" biblio-lookup "From biblio" :column "Automatic") - ;; Bibtex types - ("ma" bibtex-Article "Article" :column "Manual") - ("mb" bibtex-Book "Book" :column "Manual") - ("mi" bibtex-InBook "In book" :column "Manual") - ("ml" bibtex-Booklet "Booklet" :column "Manual") - ("mP" bibtex-Proceedings "Proceedings" :column "Manual") - ("mp" bibtex-InProceedings "In proceedings" :column "Manual") - ("mm" bibtex-Misc "Misc." :column "Manual") - ("mM" bibtex-Manual "Manual" :column "Manual") - ("mT" bibtex-PhdThesis "PhD Thesis" :column "Manual") - ("mt" bibtex-MastersThesis "MS Thesis" :column "Manual") - ("mR" bibtex-TechReport "Report" :column "Manual") - ("mu" bibtex-Unpublished "unpublished" :column "Manual") - ("mc" bibtex-InCollection "Article in collection" :column "Manual") - ("q" nil "quit")) - - -;;** Hydra menu of functions to act on a bibtex file. -(defhydra org-ref-bibtex-file (:color blue) - "Bibtex file functions: " - ("v" bibtex-validate "Validate entries") - ("s" bibtex-sort-buffer "Sort entries") - ("r" bibtex-reformat "Reformat entries") - ("c" bibtex-count-entries "Count entries") - ("p" org-ref-build-full-bibliography "PDF bibliography")) +;;** Transient menu for new bibtex entries +;; A transient for adding new bibtex entries. +(transient-define-prefix org-ref-bibtex-new-entry-menu () + "New Bibtex entry." + [["Automatic" + ("d" "from DOI" doi-insert-bibtex) + ("c" "from Crossref" crossref-add-bibtex-entry) + ("a" "From Arxiv" arxiv-add-bibtex-entry) + ("b" "From biblio" biblio-lookup)] + ["Manual" + ("ma" "Article" bibtex-Article) + ("mb" "Book" bibtex-Book) + ("mi" "In book" bibtex-InBook) + ("ml" "Booklet" bibtex-Booklet) + ("mP" "Proceedings" bibtex-Proceedings) + ("mp" "In proceedings" bibtex-InProceedings) + ("mm" "Misc." bibtex-Misc) + ("mM" "Manual" bibtex-Manual) + ("mT" "PhD Thesis" bibtex-PhdThesis) + ("mt" "MS Thesis" bibtex-MastersThesis) + ("mR" "Report" bibtex-TechReport) + ("mu" "unpublished" bibtex-Unpublished) + ("mc" "Article in collection" bibtex-InCollection) + ("q" "Quit" transient-quit-one)]]) + + +;;** Transient menu of functions to act on a bibtex file. +(transient-define-prefix org-ref-bibtex-file-menu () + "Bibtex file functions." + [["Commands" + ("v" "Validate entries" bibtex-validate) + ("s" "Sort entries" bibtex-sort-buffer) + ("r" "Reformat entries" bibtex-reformat) + ("c" "Count entries" bibtex-count-entries) + ("p" "PDF bibliography" org-ref-build-full-bibliography) + ("q" "Quit" transient-quit-one)]]) + +(define-obsolete-function-alias 'org-ref-bibtex-hydra/body + #'org-ref-bibtex-entry-menu "3.1") +(define-obsolete-function-alias 'org-ref-bibtex-new-entry/body + #'org-ref-bibtex-new-entry-menu "3.1") +(define-obsolete-function-alias 'org-ref-bibtex-file/body + #'org-ref-bibtex-file-menu "3.1") ;;* Email a bibtex entry @@ -1022,7 +1024,7 @@ keywords. Optional argument ARG prefix arg to replace keywords." (cl-loop for buffer in (buffer-list) do (with-current-buffer buffer - (when (and (buffer-file-name) (f-ext? (buffer-file-name) "bib")) + (when (and (buffer-file-name) (org-ref--file-ext-p (buffer-file-name) "bib")) (save-buffer))))) @@ -1150,7 +1152,7 @@ will clobber the file." ;;these are the other fields in the entry, and we sort them alphabetically. (setq other-fields - (sort (-remove (lambda(x) (member x field-order)) entry-fields) + (sort (cl-remove-if (lambda(x) (member x field-order)) entry-fields) 'string<)) (save-restriction @@ -1170,7 +1172,7 @@ will clobber the file." (cl-loop for (f . v) in entry concat (when (string= f field) (format "%s = %s,\n" f v)))) - (-uniq other-fields) "\n") + (delete-dups other-fields) "\n") "\n}")) (bibtex-search-entry key) (bibtex-fill-entry) @@ -1290,7 +1292,7 @@ If optional NEW-YEAR set it to that, otherwise prompt for it." (defun orcb-& () - "Replace naked & with \& in a bibtex entry." + "Replace naked & with \\& in a bibtex entry." (save-restriction (bibtex-narrow-to-entry) (bibtex-beginning-of-entry) @@ -1365,7 +1367,7 @@ If not, issue a warning." (journal (cdr (assoc "journal" entry)))) (when (null journal) (warn "Unable to get journal for this entry.")) - (unless (member journal (-flatten org-ref-bibtex-journal-abbreviations)) + (unless (member journal (org-ref--flatten-list org-ref-bibtex-journal-abbreviations)) (message "Journal \"%s\" not found in org-ref-bibtex-journal-abbreviations." journal)))))) diff --git a/elpa/org-ref-20241124.2012/org-ref-citation-links.el b/elpa/org-ref-20251206.1422/org-ref-citation-links.el similarity index 91% rename from elpa/org-ref-20241124.2012/org-ref-citation-links.el rename to elpa/org-ref-20251206.1422/org-ref-citation-links.el index d030746e..8c308e06 100644 --- a/elpa/org-ref-20241124.2012/org-ref-citation-links.el +++ b/elpa/org-ref-20251206.1422/org-ref-citation-links.el @@ -28,8 +28,8 @@ ;; indicate the pre/post-note structure. They also have tooltips that show ;; information from the bibtex entry. ;; -;; Each link is functional, and clicking on one will open a hydra menu -;; `org-ref-citation-hydra/body' of actions that range from opening the bibtex +;; Each link is functional, and clicking on one will open a transient menu +;; `org-ref-citation-menu' of actions that range from opening the bibtex ;; entry, notes, pdf or associated URL, to searching the internet for related ;; articles. ;; @@ -48,7 +48,7 @@ ;; ;;; Code: (require 'org-keys) -(require 'hydra) +(require 'transient) (require 'xref) (eval-when-compile (require 'subr-x)) @@ -340,14 +340,14 @@ to a path string." (string-join (cl-loop for ref in (plist-get data :references) collect (plist-get ref :key)) ",")) (3 (concat - (when-let (prefix (plist-get data :prefix)) (concat prefix ";")) + (when-let* ((prefix (plist-get data :prefix))) (concat prefix ";")) (string-join (cl-loop for ref in (plist-get data :references) collect (concat (plist-get ref :prefix) "&" (plist-get ref :key) (plist-get ref :suffix))) ";") - (when-let (suffix (plist-get data :suffix)) (concat ";" suffix)))))) + (when-let* ((suffix (plist-get data :suffix))) (concat ";" suffix)))))) ;; * Activating citation links @@ -360,19 +360,6 @@ to a path string." (defvar bibtex-completion-bibliography) (defvar bibtex-completion-display-formats-internal) -;; (defun org-ref-valid-keys () -;; "Return a list of valid bibtex keys for this buffer. -;; This is used a lot in `org-ref-cite-activate' so it needs to be -;; fast, but also up to date." -;; ;; this seems to be needed, but we don't want to do this every time -;; (unless bibtex-completion-display-formats-internal -;; (bibtex-completion-init)) - -;; (let ((bibtex-completion-bibliography (org-ref-find-bibliography))) -;; (cl-loop for entry in (bibtex-completion-candidates) -;; collect -;; (cdr (assoc "=key=" (cdr entry)))))) - (defun org-ref-valid-keys () "Return a list of valid bibtex keys for this buffer. @@ -380,6 +367,8 @@ This is used a lot in `org-ref-cite-activate' so it needs to be fast, but also up to date." ;; this seems to be needed, but we don't want to do this every time + ;; I found when bibtex-completion-display-formats-internal is nil + ;; we have to run this init function (unless bibtex-completion-display-formats-internal (bibtex-completion-init)) @@ -394,7 +383,9 @@ fast, but also up to date." for file in files append (cddr (assoc file bibtex-completion-cache))) collect (cdr (assoc "=key=" (cdr entry)))) - ;; you need to get a cache because one or more of the files was not in the cache. + ;; you need to get a cache because one or more of the files was not in the + ;; cache. The cache should be automatically made by + ;; bibtex-completion-candidates (let ((bibtex-completion-bibliography files)) (cl-loop for entry in (bibtex-completion-candidates) collect @@ -405,11 +396,15 @@ fast, but also up to date." (defvar-local org-ref-valid-keys-cache nil) (defun org-ref-valid-keys-cached () - "Update `org-ref-valid-keys-cache` only when files changed." + "Update `org-ref-valid-keys-cache` only when files changed or it is empty. +Returns a hash-table you can use to test key validity. + +(gethash key (org-ref-valid-keys-cached)" (let ((local-hashes (cons bibtex-completion-bibliography (mapcar 'cadr bibtex-completion-cache)))) - (when (not (equal local-hashes org-ref-valid-keys-hashes)) + (when (or (null org-ref-valid-keys-cache) + (not (equal local-hashes org-ref-valid-keys-hashes))) (setq-local org-ref-valid-keys-hashes local-hashes) (setq-local org-ref-valid-keys-cache (make-hash-table :test 'equal)) (cl-loop for entry in (org-ref-valid-keys) @@ -427,7 +422,7 @@ PATH has the citations in it." ;; path containing @ which makes it likely to be an org-cite. Maybe ;; a text property is better, in case this is an issue in the ;; future. - (not (s-contains-p "@" path))) + (not (string-match-p (regexp-quote "@") path))) (let* ((valid-keys (org-ref-valid-keys)) valid-key substrings) @@ -522,64 +517,67 @@ PATH has the citations in it." (declare-function org-ref-get-bibtex-key-and-file "org-ref-core") -(defhydra org-ref-citation-hydra (:color blue :hint nil) - "Citation actions -" - ("o" org-ref-open-citation-at-point "Bibtex" :column "Open") - ("p" org-ref-open-pdf-at-point "PDF" :column "Open") - ("n" org-ref-open-notes-at-point "Notes" :column "Open") - ("u" org-ref-open-url-at-point "URL" :column "Open") - - ;; WWW actions - ("ww" org-ref-wos-at-point "WOS" :column "WWW") - ("wr" org-ref-wos-related-at-point "WOS related" :column "WWW") - ("wc" org-ref-wos-citing-at-point "WOS citing" :column "WWW") - ("wg" org-ref-google-scholar-at-point "Google Scholar" :column "WWW") - ("wp" org-ref-pubmed-at-point "Pubmed" :column "WWW") - ("wf" org-ref-crossref-at-point "Crossref" :column "WWW") - ("wb" org-ref-biblio-at-point "Biblio" :column "WWW") - ("e" org-ref-email-at-point "Email" :column "WWW") - - ;; Copyish actions - ("K" (save-window-excursion - (let ((bibtex-completion-bibliography (org-ref-find-bibliography))) - (bibtex-completion-show-entry (list (org-ref-get-bibtex-key-under-cursor))) - (bibtex-copy-entry-as-kill) - (kill-new (pop bibtex-entry-kill-ring)))) - "Copy bibtex" :column "Copy") - ("a" org-ref-add-pdf-at-point "add pdf to library" :column "Copy") - ("k" (kill-new (car (org-ref-get-bibtex-key-and-file))) "Copy key" :column "Copy") - ("f" (kill-new (bibtex-completion-apa-format-reference - (org-ref-get-bibtex-key-under-cursor))) - "Copy formatted" :column "Copy") - ("h" (kill-new - (format "* %s\n\n cite:&%s" - (bibtex-completion-apa-format-reference - (org-ref-get-bibtex-key-under-cursor)) - (car (org-ref-get-bibtex-key-and-file)))) - "Copy org heading" - :column "Copy") - - ;; Editing actions - ("" org-ref-cite-shift-left "Shift left" :color red :column "Edit") - ("" org-ref-cite-shift-right "Shift right" :color red :column "Edit") - ("" org-ref-sort-citation-link "Sort by year" :column "Edit") - ("i" (funcall org-ref-insert-cite-function) "Insert cite" :column "Edit") - ("t" org-ref-change-cite-type "Change cite type" :column "Edit") - ("d" org-ref-delete-citation-at-point "Delete at point" :column "Edit") - ("r" org-ref-replace-citation-at-point "Replace cite" :column "Edit") - ("P" org-ref-edit-pre-post-notes "Edit pre/suffix" :column "Edit") - - ;; Navigation - ("[" org-ref-previous-key "Previous key" :column "Navigation" :color red) - ("]" org-ref-next-key "Next key" :column "Navigation" :color red) - ("v" org-ref-jump-to-visible-key "Visible key" :column "Navigation" :color red) - ("q" nil "Quit")) +(transient-define-prefix org-ref-citation-menu () + "Citation actions." + [["Open" + ("o" "Bibtex" org-ref-open-citation-at-point) + ("p" "PDF" org-ref-open-pdf-at-point) + ("n" "Notes" org-ref-open-notes-at-point) + ("u" "URL" org-ref-open-url-at-point)] + ["WWW" + ("ww" "WOS" org-ref-wos-at-point) + ("wr" "WOS related" org-ref-wos-related-at-point) + ("wc" "WOS citing" org-ref-wos-citing-at-point) + ("wg" "Google Scholar" org-ref-google-scholar-at-point) + ("wp" "Pubmed" org-ref-pubmed-at-point) + ("wf" "Crossref" org-ref-crossref-at-point) + ("wb" "Biblio" org-ref-biblio-at-point) + ("e" "Email" org-ref-email-at-point)] + ["Copy" + ("K" "Copy bibtex" (lambda () + (interactive) + (save-window-excursion + (let ((bibtex-completion-bibliography (org-ref-find-bibliography))) + (bibtex-completion-show-entry (list (org-ref-get-bibtex-key-under-cursor))) + (bibtex-copy-entry-as-kill) + (kill-new (pop bibtex-entry-kill-ring)))))) + ("a" "Add pdf to library" org-ref-add-pdf-at-point) + ("k" "Copy key" (lambda () + (interactive) + (kill-new (car (org-ref-get-bibtex-key-and-file))))) + ("f" "Copy formatted" (lambda () + (interactive) + (kill-new (bibtex-completion-apa-format-reference + (org-ref-get-bibtex-key-under-cursor))))) + ("h" "Copy org heading" (lambda () + (interactive) + (kill-new + (format "* %s\n\n cite:&%s" + (bibtex-completion-apa-format-reference + (org-ref-get-bibtex-key-under-cursor)) + (car (org-ref-get-bibtex-key-and-file))))))] + ["Edit" + ("" "Shift left" org-ref-cite-shift-left :transient t) + ("" "Shift right" org-ref-cite-shift-right :transient t) + ("" "Sort by year" org-ref-sort-citation-link) + ("i" "Insert cite" (lambda () (interactive) (funcall org-ref-insert-cite-function))) + ("t" "Change cite type" org-ref-change-cite-type) + ("d" "Delete at point" org-ref-delete-citation-at-point) + ("r" "Replace cite" org-ref-replace-citation-at-point) + ("P" "Edit pre/suffix" org-ref-edit-pre-post-notes)] + ["Navigation" + ("[" "Previous key" org-ref-previous-key :transient t) + ("]" "Next key" org-ref-next-key :transient t) + ("v" "Visible key" org-ref-jump-to-visible-key :transient t) + ("q" "Quit" transient-quit-one)]]) (defun org-ref-cite-follow (_path) "Follow a cite link." - (org-ref-citation-hydra/body)) + (org-ref-citation-menu)) + +(define-obsolete-function-alias 'org-ref-citation-hydra/body + #'org-ref-citation-menu "3.1") ;; * Citation links tooltips @@ -637,13 +635,13 @@ Use with apply-partially." (format "[%s]" (cl-second prefix-suffix))) (t "")))) - (s-format "\\${cmd}${prefix}${suffix}{${keys}}" 'aget + (org-ref--format-template "\\${cmd}${prefix}${suffix}{${keys}}" `(("cmd" . ,cmd) ("prefix" . ,(string-trim prefix)) ("suffix" . ,(string-trim suffix)) ("keys" . ,(string-join keys ",")))))) (3 - (s-format "\\${cmd}${prefix}${suffix}{${keys}}" 'aget + (org-ref--format-template "\\${cmd}${prefix}${suffix}{${keys}}" `(("cmd" . ,cmd) ;; if there is more than one key, we only do global ;; prefix/suffix But for one key, we should allow local @@ -748,7 +746,7 @@ Use with apply-partially." (pcase backend ('latex (let ((cite (org-ref-parse-cite-path path))) - (s-format "\\${cmd}${global-prefix}${global-suffix}${keys}" 'aget + (org-ref--format-template "\\${cmd}${global-prefix}${global-suffix}${keys}" `(("cmd" . ,cmd) ("global-prefix" . ,(cond ((plist-get cite :prefix) @@ -858,7 +856,7 @@ Use with apply-partially." (setq i (seq-position references key (lambda (el key) (string= key (plist-get el :key))))) ;; delete i'th reference - (setq references (-remove-at i references)) + (setq references (org-ref--remove-at i references)) (setq data (plist-put data :references references)) (save-excursion (goto-char begin) @@ -1006,7 +1004,7 @@ arg COMMON, edit the common prefixes instead." If not on a key, but on a cite, prompt for key." (cond (org-ref-activate-cite-links - (if-let ((key (get-text-property (point) 'cite-key))) + (if-let* ((key (get-text-property (point) 'cite-key))) ;; Point is on a key, so we get it directly key ;; point is not on a key, but may still be on a cite link @@ -1046,10 +1044,10 @@ If not on a key, but on a cite, prompt for key." (prog1 (get-text-property (point) 'cite-key) (goto-char cp))))))))) - + ;; org-ref-activate-cite-links is nil so font-lock does not put ;; text-properties on keys. We temporarily activate this - + (t (let ((el (org-element-context)) (org-ref-activate-cite-links t)) ;; temporary @@ -1171,10 +1169,10 @@ If not on a key, but on a cite, prompt for key." Otherwise run `right-word'. If the cursor moves off the link, move to the beginning of the next cite link after this one." (interactive) - (when-let (next (next-single-property-change (point) 'cite-key)) + (when-let* ((next (next-single-property-change (point) 'cite-key))) (goto-char next)) (unless (get-text-property (point) 'cite-key) - (when-let (next (next-single-property-change (point) 'cite-key)) + (when-let* ((next (next-single-property-change (point) 'cite-key))) (goto-char next)))) @@ -1184,10 +1182,10 @@ move to the beginning of the next cite link after this one." Otherwise run `left-word'. If the cursor moves off the link, move to the beginning of the previous cite link after this one." (interactive) - (when-let (prev (previous-single-property-change (point) 'cite-key)) + (when-let* ((prev (previous-single-property-change (point) 'cite-key))) (goto-char prev)) (unless (get-text-property (point) 'cite-key) - (when-let (prev (previous-single-property-change (point) 'cite-key)) + (when-let* ((prev (previous-single-property-change (point) 'cite-key))) (goto-char prev)))) (defvar avy-goto-key) @@ -1316,7 +1314,7 @@ Rules: (string= key-at-point (plist-get el1 :key))))) (setq data (plist-put data :references - (-insert-at + (org-ref--insert-at (+ index (if (and (= 3 version) (looking-at "&")) 0 1)) diff --git a/elpa/org-ref-20241124.2012/org-ref-compat.el b/elpa/org-ref-20251206.1422/org-ref-compat.el similarity index 100% rename from elpa/org-ref-20241124.2012/org-ref-compat.el rename to elpa/org-ref-20251206.1422/org-ref-compat.el diff --git a/elpa/org-ref-20241124.2012/org-ref-core.el b/elpa/org-ref-20251206.1422/org-ref-core.el similarity index 66% rename from elpa/org-ref-20241124.2012/org-ref-core.el rename to elpa/org-ref-20251206.1422/org-ref-core.el index c7b34ded..e1907452 100644 --- a/elpa/org-ref-20241124.2012/org-ref-core.el +++ b/elpa/org-ref-20251206.1422/org-ref-core.el @@ -35,13 +35,11 @@ (require 'org-element) -(require 'dash) -(require 'f) -(require 's) +(require 'org-ref-utils) (require 'parsebib) (require 'bibtex-completion) -(require 'hydra) +(require 'transient) (require 'org-ref-bibliography-links) (require 'org-ref-citation-links) @@ -172,9 +170,7 @@ set in `bibtex-completion-bibliography'" (throw 'result (nreverse (delete-dups (mapcar 'org-ref-get-bibfile-path org-ref-bibliography-files))))))) ;; we did not find anything. use defaults. Make sure we have a list in ;; case it is a single string. - (throw 'result (if (listp bibtex-completion-bibliography) - bibtex-completion-bibliography - (list bibtex-completion-bibliography))))))) + (throw 'result (org-ref-normalize-bibtex-completion-bibliography)))))) (defun org-ref-key-in-file-p (key filename) @@ -188,18 +184,16 @@ set in `bibtex-completion-bibliography'" (defun org-ref-possible-bibfiles () "Make a unique list of possible bibliography files for completing-read" - (-uniq + (delete-dups (append ;; see if we should add it to a bib-file defined in the file (org-ref-find-bibliography) ;; or any bib-files that exist in the current directory - (f-entries "." (lambda (f) + (org-ref--directory-files "." (lambda (f) (and (not (string-match "#" f)) - (f-ext? f "bib")))) + (org-ref--file-ext-p f "bib")))) ;; and last in the default bibliography - (if (stringp bibtex-completion-bibliography) - (list bibtex-completion-bibliography) - bibtex-completion-bibliography)))) + (org-ref-normalize-bibtex-completion-bibliography)))) (defun org-ref-get-bibtex-key-and-file (&optional key) @@ -236,65 +230,106 @@ provide their own version." ;; This is an alternative that doesn't rely on prefix args. -(defhydra org-ref-insert-link-hydra (:color red :hint nil) - "Insert an org-ref link -" - ("[" (funcall org-ref-insert-cite-function) "Citation" :column "org-ref") - ("]" (funcall org-ref-insert-ref-function) "Cross-reference" :column "org-ref") - ("\\" (funcall org-ref-insert-label-function) "Label" :column "org-ref") - - ("bs" (insert (org-ref-bibliographystyle-complete-link)) "Bibliographystyle" :column "Bibliography" :color blue) - ("bf" (insert (org-ref-bibliography-complete)) "Bibliography" :column "Bibliography" :color blue) - ("nb" (insert (org-ref-nobibliography-complete)) "Bibliography" :column "Bibliography" :color blue) - - ("g" org-ref-insert-glossary-link "Glossary link" :column "Glossary" :color blue) - ("a" org-ref-insert-acronym-link "Acronym link" :column "Glossary" :color blue) - ("ng" (progn - (org-mark-ring-push) - (goto-char (point-min)) - (if (re-search-forward "#\\+name: glossary" nil t) - (progn - (goto-char (org-element-property :contents-end (org-element-context))) - (backward-char) - (org-table-insert-row '(4))) - ;; no table found - (goto-char (point-max)) - (insert "\n\n#+name: glossary +(defun org-ref-insert-link-menu--insert-citation () + (interactive) + (funcall org-ref-insert-cite-function)) + +(defun org-ref-insert-link-menu--insert-reference () + (interactive) + (funcall org-ref-insert-ref-function)) + +(defun org-ref-insert-link-menu--insert-label () + (interactive) + (funcall org-ref-insert-label-function)) + +(defun org-ref-insert-link-menu--open-bibliography () + (interactive) + (find-file (completing-read "Bibliography: " (org-ref-find-bibliography)))) + +(defun org-ref-insert-link-menu--ensure-table (name template) + (org-mark-ring-push) + (goto-char (point-min)) + (if (re-search-forward (format "#\\+name: %s" name) nil t) + (progn + (goto-char (org-element-property :contents-end (org-element-context))) + (backward-char) + (org-table-insert-row '(4))) + (goto-char (point-max)) + (insert template) + (beginning-of-line) + (forward-char))) + +(defun org-ref-insert-link-menu--new-glossary-term () + (interactive) + (org-ref-insert-link-menu--ensure-table + "glossary" + "\n\n#+name: glossary | label | term | definition | |-------+---------+-------------------------------| -| | | |") - (beginning-of-line) - (forward-char))) - "New glossary term" :column "Glossary") - - ("na" (progn - (org-mark-ring-push) - (goto-char (point-min)) - (if (re-search-forward "#\\+name: acronym" nil t) - (progn - (goto-char (org-element-property :contents-end (org-element-context))) - (backward-char) - (org-table-insert-row '(4))) - ;; no table found - (goto-char (point-max)) - (insert "\n\n#+name: acronyms +| | | |")) + +(defun org-ref-insert-link-menu--new-acronym-term () + (interactive) + (org-ref-insert-link-menu--ensure-table + "acronym" + "\n\n#+name: acronyms | label | abbreviation | full form | |-------+--------------+----------------------------| -| | | |") - (beginning-of-line) - (forward-char))) - "New acronym term" :column "Glossary") - - ("bd" doi-add-bibtex-entry "Add bibtex entry from a DOI" :column "Bibtex") - ("bc" crossref-add-bibtex-entry "Add bibtex entry from Crossref" :column "Bibtex") - ("bo" (find-file (completing-read "Bibliography: " (org-ref-find-bibliography))) - "Open bibtex file" :column "Bibtex") - - ("t" (insert "[[list-of-tables:]]\n") "List of tables" :column "Misc") - ("f" (insert "[[list-of-figures:]]\n") "List of figures" :column "Misc") - ("i" (insert (format "[[index:%s]]" (string-trim (read-string "Index entry: ")))) "Index entry" :column "Misc") - ("pi" (insert "[[printindex:]]") "Print index" :column "Misc") - ("pg" (insert "[[printglossaries:]]") "Print glossary" :column "Misc")) +| | | |")) + +(defun org-ref-insert-link-menu--insert-string (string) + (insert string)) + +(defun org-ref-insert-link-menu--insert-index (prompt template) + (org-ref-insert-link-menu--insert-string + (format template (string-trim (read-string prompt))))) + +(transient-define-prefix org-ref-insert-link-menu () + "Insert an org-ref link." + [["org-ref" + ("]" "Citation" org-ref-insert-link-menu--insert-citation :transient t) + ("r" "Cross-reference" org-ref-insert-link-menu--insert-reference :transient t) + ("\\" "Label" org-ref-insert-link-menu--insert-label :transient t)] + ["Bibliography" + ("bs" "Bibliographystyle" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string + (org-ref-bibliographystyle-complete-link)))) + ("bf" "Bibliography" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string + (org-ref-bibliography-complete)))) + ("nb" "Nobibliography" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string + (org-ref-nobibliography-complete))))] + ["Glossary" + ("g" "Glossary link" org-ref-insert-glossary-link) + ("a" "Acronym link" org-ref-insert-acronym-link) + ("ng" "New glossary term" org-ref-insert-link-menu--new-glossary-term :transient t) + ("na" "New acronym term" org-ref-insert-link-menu--new-acronym-term :transient t)] + ["Bibtex" + ("bd" "Add bibtex entry from a DOI" doi-add-bibtex-entry :transient t) + ("bc" "Add bibtex entry from Crossref" crossref-add-bibtex-entry :transient t) + ("bo" "Open bibtex file" org-ref-insert-link-menu--open-bibliography :transient t)] + ["Misc" + ("t" "List of tables" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string "[[list-of-tables:]]\n")) + :transient t) + ("f" "List of figures" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string "[[list-of-figures:]]\n")) + :transient t) + ("i" "Index entry" (lambda () (interactive) + (org-ref-insert-link-menu--insert-index + "Index entry: " "[[index:%s]]")) + :transient t) + ("pi" "Print index" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string "[[printindex:]]")) + :transient t) + ("pg" "Print glossary" (lambda () (interactive) + (org-ref-insert-link-menu--insert-string "[[printglossaries:]]")) + :transient t) + ("q" "Quit" transient-quit-one)]]) + +(define-obsolete-function-alias 'org-ref-insert-link-hydra/body + #'org-ref-insert-link-menu "3.1") ;;* org-ref-help diff --git a/elpa/org-ref-20241124.2012/org-ref-export.el b/elpa/org-ref-20251206.1422/org-ref-export.el similarity index 62% rename from elpa/org-ref-20241124.2012/org-ref-export.el rename to elpa/org-ref-20251206.1422/org-ref-export.el index 983d47ec..f2ed26e4 100644 --- a/elpa/org-ref-20241124.2012/org-ref-export.el +++ b/elpa/org-ref-20251206.1422/org-ref-export.el @@ -39,18 +39,36 @@ ;; export is your goal, you may want to use org-ref-refproc.el to handle ;; cross-references. ;; +;;; Formatting Limitations: +;; +;; Different export backends have different formatting capabilities. HTML and +;; LaTeX support full CSL formatting including small caps, italics, bold, etc. +;; +;; The org-mode backend has limitations because org-mode syntax does not support +;; small caps. Many CSL citation styles use small caps for author names, so these +;; will not render correctly when exporting to formats that use the org backend +;; (like DOCX by default). +;; +;; For DOCX export with better formatting support, set +;; `org-ref-docx-use-html-backend' to t. This causes citations to be rendered +;; as HTML which pandoc can convert to DOCX while preserving formatting. +;; +;; See `org-ref-backend-csl-formats' to customize backend mappings for different +;; export formats. +;; ;;; Code: -(eval-when-compile - (require 'hydra)) +(require 'transient) +(require 'org-ref-utils) +(require 'org-ref-citation-links) (defvar hfy-user-sheet-assoc) ; to quiet compiler (require 'ox-org) -(if (executable-find "pandoc") - (require 'ox-pandoc)) +(when (executable-find "pandoc") + (ignore-errors (require 'ox-pandoc))) -(require 'citeproc) +(ignore-errors (require 'citeproc)) (defvar org-cite-csl-styles-dir) @@ -62,7 +80,24 @@ (ascii . plain) (odt . org-odt) (docx . org)) - "Mapping of export backend to csl-backends." + "Mapping of export backend to csl-backends. + +Each entry maps an org export backend symbol to a citeproc-el backend +format used for rendering citations and bibliographies. + +Note: The 'org backend has formatting limitations because org-mode +syntax does not support small caps. CSL styles that use small caps +(common for author names) will not render correctly when using the +'org backend. For DOCX export, consider setting +`org-ref-docx-use-html-backend' to t to use HTML formatting which +pandoc can convert while preserving small caps and other formatting. + +Available citeproc backends: +- html: Full HTML formatting with CSS styles +- latex: Full LaTeX formatting with commands like \\textsc{} +- org: Org-mode markup (limited, no small caps support) +- org-odt: ODT-specific XML formatting +- plain: Plain text with no formatting" :type '(alist :key-type (symbol) :value-type (symbol)) :group 'org-ref) @@ -92,6 +127,30 @@ Should be a csl filename, or an absolute path to a csl filename." :group 'org-ref) +(defcustom org-ref-docx-use-html-backend nil + "Use HTML backend for DOCX export to preserve CSL formatting. + +When non-nil, citations exported to DOCX will be rendered using the +HTML backend, which preserves formatting like small caps and italics +that pandoc can convert to DOCX. + +When nil (default), uses the org backend which has limited formatting +support due to org-mode syntax limitations. Specifically, org-mode has +no native syntax for small caps, so CSL styles that use small caps +(common for author names in many citation styles) will not render +correctly in DOCX output. + +Note: This option only affects DOCX export via pandoc. Other export +formats (HTML, LaTeX, ODT) are not affected. + +See also `org-ref-backend-csl-formats' to customize backend mappings +for other export formats. + +Related to issue #981: https://github.com/jkitchin/org-ref/issues/981" + :type 'boolean + :group 'org-ref) + + (defcustom org-ref-csl-label-aliases '((("app" "apps") . "appendix") (("art" "arts") . "article-locator") @@ -128,7 +187,7 @@ See https://github.com/citation-style-language/documentation/blob/master/specifi :group 'org-ref) -(defcustom org-ref-export-suppress-affix-types +(defcustom org-ref-export-suppress-affix-types '("citet" "citet*" "citetitle" @@ -141,6 +200,68 @@ See https://github.com/citation-style-language/documentation/blob/master/specifi :group 'org-ref) +;;; Footnote citation support (issue #993) + +(defvar org-ref-footnote-counter 0 + "Counter for footnote citations during export. +Reset to 0 at the start of each export process.") + +(defcustom org-ref-footnote-cite-types + '("footcite" + "footfullcite" + "footcitetext" + "footcites" + "footcitetexts" + "smartcite" + "Smartcite") + "List of citation types that should be rendered as footnotes. +These citation types will be given a :note-index parameter when +passed to citeproc-el, which causes them to be rendered as notes/footnotes +when using CSL styles that support note formatting." + :type '(repeat string) + :group 'org-ref) + + +(defun org-ref-footnote-cite-type-p (cite-type) + "Return non-nil if CITE-TYPE should be rendered as a footnote. +CITE-TYPE is a string like \"footcite\" or \"cite\"." + (member cite-type org-ref-footnote-cite-types)) + + +(defun org-ref-get-next-footnote-number () + "Get the next footnote number and increment the counter. +Returns the next sequential footnote number (1, 2, 3, ...)." + (setq org-ref-footnote-counter (1+ org-ref-footnote-counter))) + + +(defun org-ref-csl-style-supports-notes-p (csl-style-file) + "Return non-nil if CSL-STYLE-FILE supports note/footnote formatting. +CSL-STYLE-FILE is a filename like \"chicago-fullnote-bibliography.csl\". + +This function checks if the CSL style file contains note-related +class attributes, which indicate it supports footnote formatting." + (or + ;; Check filename for common note-style indicators + ;; Match: -note-, -note.csl, fullnote, footnote + (string-match-p "\\(\\bnote\\b\\|fullnote\\|footnote\\)" csl-style-file) + ;; If we have the actual file, check its contents + (when (file-exists-p csl-style-file) + (with-temp-buffer + (insert-file-contents csl-style-file) + (goto-char (point-min)) + ;; Look for class="note" in the CSL XML + (re-search-forward "class=\"note\"" nil t))))) + + +(defun org-ref-get-citation-note-index (citation-link) + "Get the note-index for CITATION-LINK if it's a footnote citation type. +CITATION-LINK is an org-element link object. +Returns a footnote number if this is a footnote citation, nil otherwise." + (when (org-ref-footnote-cite-type-p + (org-element-property :type citation-link)) + (org-ref-get-next-footnote-number))) + + (defun org-ref-dealias-label (alias) "Return the full, de-aliased label for ALIAS. Looked up from `org-ref-csl-label-aliases'. @@ -173,7 +294,7 @@ REF is a plist data structure returned from `org-ref-parse-cite-path'." ;; and the rest is the suffix text. ;; For example: ch. 5, for example ;; would be label = ch., locator=5, ",for example" as suffix. - (let* ((full-suffix (string-trim (or (plist-get ref :suffix) ""))) + (let* ((full-suffix (string-trim (or (plist-get ref :suffix) ""))) locator label locator suffix) @@ -196,10 +317,10 @@ REF is a plist data structure returned from `org-ref-parse-cite-path'." ;; number or numeric ranges (group-n 2 (one-or-more digit) (optional "-" (one-or-more digit))) ;; everything else - (group-n 3 (* ".")))) + (group-n 3 (* ".")))) full-suffix) (match-string 2 full-suffix) - (not (string= "" (match-string 2 full-suffix)))) + (not (string= "" (match-string 2 full-suffix)))) ;; We found a locator (setq label (match-string 1 full-suffix) locator (match-string 2 full-suffix) @@ -207,11 +328,11 @@ REF is a plist data structure returned from `org-ref-parse-cite-path'." (setq label nil locator nil suffix full-suffix)) - + ;; Let's assume if you have a locator but not a label that you mean page. (when (and locator (string= "" (string-trim label))) (setq label "page")) - + `((id . ,(plist-get ref :key)) (prefix . ,(plist-get ref :prefix)) (suffix . ,suffix) @@ -234,10 +355,17 @@ REF is a plist data structure returned from `org-ref-parse-cite-path'." "Process the citations and bibliography in the org-buffer. Usually run on a copy of the buffer during export. BACKEND is the org export backend." + ;; Reset footnote counter for this export + (setq org-ref-footnote-counter 0) + (save-restriction (when subtreep (org-narrow-to-subtree)) (let* ((csl-backend (or (cdr (assoc backend org-ref-backend-csl-formats)) 'plain)) + ;; Use HTML backend for DOCX if requested for better formatting + (csl-backend (if (and (eq backend 'docx) org-ref-docx-use-html-backend) + 'html + csl-backend)) (style (or (cadr (assoc "CSL-STYLE" (org-collect-keywords @@ -248,6 +376,24 @@ BACKEND is the org export backend." '("CSL-LOCALE")))) org-ref-csl-default-locale)) + ;; Determine the actual style file path for checking note support + (style-file (cond + ((file-exists-p style) + style) + ((and (bound-and-true-p org-cite-csl-styles-dir) + (file-exists-p (org-ref--file-join org-cite-csl-styles-dir style))) + (org-ref--file-join org-cite-csl-styles-dir style)) + ((file-exists-p (expand-file-name style + (org-ref--file-join (file-name-directory + (locate-library "org-ref")) + "citeproc/csl-styles"))) + (expand-file-name style (org-ref--file-join + (file-name-directory + (locate-library "org-ref")) + "citeproc/csl-styles"))) + (t + style))) ; Fallback to style name itself + (proc (citeproc-create ;; The style (cond @@ -255,14 +401,14 @@ BACKEND is the org export backend." style) ;; In a user-dir ((and (bound-and-true-p org-cite-csl-styles-dir) - (file-exists-p (f-join org-cite-csl-styles-dir style))) - (f-join org-cite-csl-styles-dir style)) + (file-exists-p (org-ref--file-join org-cite-csl-styles-dir style))) + (org-ref--file-join org-cite-csl-styles-dir style)) ;; provided by org-ref ((file-exists-p (expand-file-name style - (f-join (file-name-directory + (org-ref--file-join (file-name-directory (locate-library "org-ref")) "citeproc/csl-styles"))) - (expand-file-name style (f-join + (expand-file-name style (org-ref--file-join (file-name-directory (locate-library "org-ref")) "citeproc/csl-styles"))) @@ -274,7 +420,7 @@ BACKEND is the org export backend." ;; locale getter (citeproc-locale-getter-from-dir (if (bound-and-true-p org-cite-csl-locales-dir) org-cite-csl-locales-dir - (f-join (file-name-directory + (org-ref--file-join (file-name-directory (locate-library "org-ref")) "citeproc/csl-locales"))) ;; the actual locale @@ -338,8 +484,15 @@ BACKEND is the org export backend." "[A-Z]" (substring (org-element-property :type cl) 0 1)) - ;; I don't know where this information would come from. - :note-index nil + ;; Set note-index for footnote citation types (issue #993) + :note-index (when (org-ref-footnote-cite-type-p + (org-element-property :type cl)) + ;; Warn if using footnote citations with non-note style + (unless (org-ref-csl-style-supports-notes-p style-file) + (warn "Citation type '%s' is a footnote citation but CSL style '%s' may not support notes. Consider using a note-based style like chicago-fullnote-bibliography.csl" + (org-element-property :type cl) + style)) + (org-ref-get-next-footnote-number)) :ignore-et-al nil :grouped nil)))) @@ -400,14 +553,14 @@ BACKEND is the org export backend." (setq s1 (format "" - ;; I hard coded this factor of 0.6 from the oc-csl code. + ;; I hard coded this factor of 0.6 from the oc-csl code. (* 0.6 (or (cdr (assq 'max-offset bib-parameters)) 0))))) ;; hard-coded the hanging indent. oc-csl uses a variable for this. I ;; guess we could too, but this seems simpler. (when (cdr (assq 'hanging-indent bib-parameters)) (setq s2 "")) - + (setq rendered-bib (concat s1 s2 rendered-bib))))) @@ -464,7 +617,7 @@ VISIBLE-ONLY BODY-ONLY and INFO." (goto-char (marker-position mm)) (org-ref-process-buffer backend subtreep) (set-marker mm nil) - + (pcase backend ;; odt is a little bit special, and is missing one argument ('odt (org-open-file (org-odt-export-to-odt async subtreep visible-only @@ -547,6 +700,11 @@ VISIBLE-ONLY BODY-ONLY and INFO." (defun org-ref-export-to-docx (&optional async subtreep visible-only body-only info) "Export the buffer to docx via pandoc and open. + +Note: By default, DOCX export uses the org backend for citations which +has formatting limitations (no small caps support). Set +`org-ref-docx-use-html-backend' to t for better formatting preservation. + See `org-export-as' for the meaning of ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY and INFO." (org-ref-export-to 'docx async subtreep visible-only @@ -614,23 +772,67 @@ I am not positive on this though." (org-at-heading-p))))) -;; A hydra exporter with preprocessors -(defhydradio org-ref () - (natmove "natmove") - (citeproc "CSL citations") - (refproc "cross-references") - (acrossproc "Acronyms, glossary") - (idxproc "Index") - (bblproc "BBL citations")) +;; A transient exporter with preprocessors +(defvar org-ref/natmove nil + "Toggle for moving natbib citations before export.") + +(defvar org-ref/citeproc nil + "Toggle for running citeproc before export.") + +(defvar org-ref/refproc nil + "Toggle for running refproc before export.") + +(defvar org-ref/acrossproc nil + "Toggle for running acrossproc before export.") + +(defvar org-ref/idxproc nil + "Toggle for running idxproc before export.") +(defvar org-ref/bblproc nil + "Toggle for running bblproc before export.") -(defun org-ref-export-from-hydra (&optional arg) - "Run the export dispatcher with the desired hooks selected in `org-ref-export/body'." +(defun org-ref-export--toggle (var) + (set var (not (symbol-value var))) + (message "%s %s" + (capitalize (replace-regexp-in-string "/" " " (symbol-name var))) + (if (symbol-value var) "enabled" "disabled")) + (symbol-value var)) + +(defun org-ref-export-toggle-natmove () + (interactive) + (org-ref-export--toggle 'org-ref/natmove)) + +(defun org-ref-export-toggle-citeproc () + (interactive) + (org-ref-export--toggle 'org-ref/citeproc)) + +(defun org-ref-export-toggle-refproc () + (interactive) + (org-ref-export--toggle 'org-ref/refproc)) + +(defun org-ref-export-toggle-acrossproc () + (interactive) + (org-ref-export--toggle 'org-ref/acrossproc)) + +(defun org-ref-export-toggle-idxproc () + (interactive) + (org-ref-export--toggle 'org-ref/idxproc)) + +(defun org-ref-export-toggle-bblproc () + (interactive) + (org-ref-export--toggle 'org-ref/bblproc)) + +(defun org-ref-export--toggle-description (label var) + (format "%s %s" label (if (symbol-value var) "[on]" "[off]"))) + + +(defun org-ref-export-dispatch (&optional arg) + "Run the export dispatcher with the desired hooks selected in the export menu." (interactive "P") (when (and org-ref/citeproc org-ref/bblproc) (error "You cannot use CSL and BBL at the same time.")) - + (let ((org-export-before-parsing-functions org-export-before-parsing-functions)) (when org-ref/citeproc (cl-pushnew 'org-ref-csl-preprocess-buffer org-export-before-parsing-functions)) @@ -648,28 +850,130 @@ I am not positive on this though." (unless (featurep 'org-ref-natbib-bbl-citeproc) (require 'org-ref-natbib-bbl-citeproc)) (cl-pushnew 'org-ref-bbl-preprocess org-export-before-parsing-functions)) - + ;; this goes last since it moves cites before they might get replaced. (when org-ref/natmove (cl-pushnew 'org-ref-cite-natmove org-export-before-parsing-functions)) (org-export-dispatch arg))) +(transient-define-prefix org-ref-export-menu () + "Select export preprocessors and dispatch export." + [["Preprocessors" + ("n" org-ref-export-toggle-natmove :transient t + :description (lambda () (org-ref-export--toggle-description "natmove" 'org-ref/natmove))) + ("c" org-ref-export-toggle-citeproc :transient t + :description (lambda () (org-ref-export--toggle-description "citeproc" 'org-ref/citeproc))) + ("r" org-ref-export-toggle-refproc :transient t + :description (lambda () (org-ref-export--toggle-description "refproc" 'org-ref/refproc))) + ("a" org-ref-export-toggle-acrossproc :transient t + :description (lambda () (org-ref-export--toggle-description "acrossproc" 'org-ref/acrossproc))) + ("i" org-ref-export-toggle-idxproc :transient t + :description (lambda () (org-ref-export--toggle-description "idxproc" 'org-ref/idxproc))) + ("b" org-ref-export-toggle-bblproc :transient t + :description (lambda () (org-ref-export--toggle-description "bblproc" 'org-ref/bblproc)))] + ["Actions" + ("e" "Export" org-ref-export-dispatch) + ("q" "Quit" transient-quit-one)]]) + +(define-obsolete-function-alias 'org-ref-export/body + #'org-ref-export-menu "3.1") + +(define-obsolete-function-alias 'org-ref-export-from-hydra + #'org-ref-export-dispatch "3.1") + +;; * exporting formatted citations +;; ** with user defined templates +(defcustom org-ref-format-templates + '(("article" . "${author} (${year}). /${title}/. ${journal}, *${volume}(${number})*, ${pages}.${doi}") + ("inproceedings" . "${author} (${year}). ${title}. In ${editor}, ${booktitle} (pp. ${pages}). ${address}: ${publisher}.") + ("book" . "${author} (${year}). ${title}. ${address}: ${publisher}.") + ("phdthesis" . "${author} (${year}). ${title} (Doctoral dissertation). ${school}, ${address}.") + ("inbook" . "${author} (${year}). ${chapter}. In ${editor} (Eds.), ${title} (pp. ${pages}). ${address}: ${publisher}.") + ("incollection" . "${author} (${year}). ${title}. In ${editor} (Eds.), ${booktitle} (pp. ${pages}). ${address}: ${publisher}.") + ("proceedings" . "${editor} (Eds.). (${year}). ${booktitle}. ${address}: ${publisher}.") + ("unpublished" . "${author} (${year}). ${title}. Unpublished manuscript.") + ("misc" . "${author} (${year}). ${title}.")) + "a-list of templates for formatting references. +The car is the BibTeX entry type, and the cdr is a template for `org-ref--format-template'.") + + +(defun org-ref-format-reference (key) + "Insert a formatted reference at point using `org-ref-format-templates'." + (let* ((entry (org-ref-bibtex-get-entry key)) + (type (cdr (assoc "=type=" entry))) + (fmt (cdr (assoc (downcase type) org-ref-format-templates))) + (result (org-ref--format-template fmt entry))) + + (setq + ;; remove ${...} patterns that were not filled in + result (replace-regexp-in-string "\\${[^}]*}" "" result) + ;; clean up multiple spaces/newlines/tabs + result (replace-regexp-in-string "[ \t\r\n]+" " " result)) + result)) + + +(defun org-ref-insert-formatted-reference () + "Insert a formatted reference at point." + (interactive) + (let* ((bibtex-completion-bibliography (org-ref-find-bibliography)) + (candidates (bibtex-completion-candidates)) + (key (completing-read "Key: " candidates + nil t)) + (entry (assoc key candidates)) + (actual-key (cdr (assoc "=key=" entry)))) + (insert (org-ref-format-reference actual-key)))) + + +;; ** with csl styles + +(defun org-ref-format-reference-csl (key &optional style-file) + "Return a formatted reference for KEY using CSL style. + STYLE-FILE is a path to a CSL style file. If nil, uses apa.csl + from the standard csl-styles location." + (let* ((bibfiles (org-ref-find-bibliography)) + (style (or style-file (expand-file-name + org-ref-csl-default-style + (org-ref--file-join (file-name-directory + (locate-library "org-ref")) + "citeproc/csl-styles")))) + ;; Create item getter from your bib files + (item-getter (citeproc-hash-itemgetter-from-any bibfiles)) + ;; Get the item data for this key + (item-data (cdr (car (funcall item-getter (list key))))) + ;; Create a minimal style object + (proc-style (citeproc-create-style + style + (citeproc-locale-getter-from-dir + (org-ref--file-join (file-name-directory + (locate-library "org-ref")) + "citeproc/csl-locales"))))) + (when item-data + ;; Render as org format with 'bib mode for bibliography-style output + (citeproc-render-item item-data proc-style 'bib 'org)))) + + +(defun org-ref-insert-formatted-reference-csl (&optional arg) + "Insert a formatted reference at point using citeproc." + (interactive "P") + (let* ((bibtex-completion-bibliography (org-ref-find-bibliography)) + (candidates (bibtex-completion-candidates)) + (ref (completing-read "Reference: " candidates + nil t)) + (entry (assoc ref candidates)) + (key (cdr (assoc "=key=" entry))) + (style (if arg + (read-file-name "CSL style file: " + (org-ref--file-join (file-name-directory + (locate-library "org-ref")) + "citeproc/csl-styles")) + nil)) + (actual-key (or key ref)) + (result (org-ref-format-reference-csl key style))) + (insert (or result + (format "Could not format reference for %s" + actual-key))))) -(defhydra org-ref-export (:color red) - " -_C-n_: natmove % -15`org-ref/natmove _C-c_: citeproc % -15`org-ref/citeproc^^^ _C-r_: refproc % -15`org-ref/refproc^^^ -_C-a_: acrossproc % -15`org-ref/acrossproc _C-i_: idxproc % -15`org-ref/idxproc^^^ _C-b_: bblproc % -15`org-ref/bblproc^^^ -" - ("C-n" (org-ref/natmove) nil) - ("C-c" (org-ref/citeproc) nil) - ("C-r" (org-ref/refproc) nil) - ("C-a" (org-ref/acrossproc) nil) - ("C-i" (org-ref/idxproc) nil) - ("C-b" (org-ref/bblproc) nil) - - ("e" org-ref-export-from-hydra "Export" :color blue) - ("q" nil "quit")) (provide 'org-ref-export) diff --git a/elpa/org-ref-20241124.2012/org-ref-extract.el b/elpa/org-ref-20251206.1422/org-ref-extract.el similarity index 94% rename from elpa/org-ref-20241124.2012/org-ref-extract.el rename to elpa/org-ref-20251206.1422/org-ref-extract.el index bbd01f1f..50ac2064 100644 --- a/elpa/org-ref-20241124.2012/org-ref-extract.el +++ b/elpa/org-ref-20251206.1422/org-ref-extract.el @@ -22,6 +22,11 @@ ;; +;;; Code: + +(declare-function org-ref-clean-bibtex-entry "org-ref-bibtex" ()) +(declare-function xml-substitute-special "xml" ()) + (defun org-ref--extract (html-buffer rx num) "Return content matched within HTML-BUFFER by RX at parenthesized @@ -104,9 +109,12 @@ and return the buffer." (interactive "MOpenReview ID: ") (let* ((url (concat "https://openreview.net/forum?id=" id)) (html-buffer (org-ref--html-buffer url))) + (with-current-buffer html-buffer + (replace-string-in-region "\\\\n" "\\n" (point-min) (point-max))) (org-ref--extract-entry-from-html html-buffer - '("\"_bibtex\":\"\\(@.+?}\\)\"" . 1) + '("\\\\\"_bibtex\\\\\":\\({\\\\\"value\\\\\":\\)?\\\\\"\\(@.+?}\\)\\\\\"" + . 2) (replace-regexp-in-string "forum" "pdf" url) '("abstract" . ("" . 1)) @@ -117,7 +125,7 @@ and return the buffer." ("\\(Summary\\|TL;DR\\).*?\"note-content-value\">\\(.+?\\)" . 2)) ;; Should we proactively download supplementary materials too? (cons "supp" - (if-let ((supp (org-ref--extract + (if-let* ((supp (org-ref--extract html-buffer ">Supplementary Material<.*?href=\"\\([^\"]+\\)" 1))) (concat "https://openreview.net" supp)))))) @@ -160,7 +168,7 @@ and return the buffer." '("abstract" . ("

Abstract

[ \n]*?\\(

\\)+\\(.+?\\)

" . 2)) ;; Should we proactively download supplementary materials too? (cons "supp" - (if-let + (if-let* ((supp (org-ref--extract html-buffer "href=[\"']\\([^\"']+-Supplemental[^\"']*\\)[\"']" 1))) diff --git a/elpa/org-ref-20241124.2012/org-ref-glossary.el b/elpa/org-ref-20251206.1422/org-ref-glossary.el similarity index 68% rename from elpa/org-ref-20241124.2012/org-ref-glossary.el rename to elpa/org-ref-20251206.1422/org-ref-glossary.el index aea320a3..7bcc4992 100644 --- a/elpa/org-ref-20241124.2012/org-ref-glossary.el +++ b/elpa/org-ref-20251206.1422/org-ref-glossary.el @@ -86,6 +86,7 @@ (require 'org-element) (require 'org-ref-utils) (require 'ox) +(require 'org-ref-ref-links) ; For multi-file support utilities ;;; Code: (defgroup org-ref-glossary nil @@ -102,6 +103,28 @@ This is not always fast, so we provide a way to disable it." :group 'org-ref-glossary) +(defcustom org-ref-glossary-show-tooltips t + "If non-nil, show tooltips when hovering over glossary and acronym links. +When nil, tooltips are disabled entirely for glossary links, which can +improve responsiveness if you find the tooltips distracting or slow. + +This is separate from `org-ref-activate-glossary-links' which controls +whether links are fontified and clickable." + :type 'boolean + :group 'org-ref-glossary) + + +(defcustom org-ref-glossary-enable-multi-file t + "Enable scanning #+INCLUDE'd files for glossary/acronym definitions. +When non-nil, glossary and acronym lookups will search in files included +via #+INCLUDE directives, enabling multi-file document support. + +Uses timestamp-based caching to maintain performance. Only files that have +changed since the last scan are re-parsed." + :type 'boolean + :group 'org-ref-glossary) + + (defcustom org-ref-glsentries '() "Variable to hold locations of glsentries load files.") @@ -114,6 +137,18 @@ This is not always fast, so we provide a way to disable it." "Buffer-local variable for acronym entry cache.") +(defvar org-ref-glossary-file-cache (make-hash-table :test 'equal) + "Global cache of glossary entries per file. +Maps file paths to lists of glossary entry plists. +Used when `org-ref-glossary-enable-multi-file' is non-nil.") + + +(defvar org-ref-acronym-file-cache (make-hash-table :test 'equal) + "Global cache of acronym entries per file. +Maps file paths to lists of acronym entry plists. +Used when `org-ref-glossary-enable-multi-file' is non-nil.") + + (defun or-find-closing-curly-bracket (&optional limit) "Find closing bracket for the bracket at point and move point to it. Go up to LIMIT or `point-max'. This is a parsing function. I @@ -160,12 +195,12 @@ changes." (let* (end-of-entry data (external (when (re-search-forward - "\\loadglsentries\\(\\[.*\\]\\){\\(?1:.*\\)}" nil t) + "\\\\loadglsentries\\(\\[.*\\]\\)?{\\(?1:.*\\)}" nil t) (match-string 1))) (glsentries (and external (or (cdr (assoc external org-ref-glsentries)) (progn - (cl-pushnew (cons external (s-trim + (cl-pushnew (cons external (string-trim (shell-command-to-string (format "kpsewhich tex %s" external)))) @@ -268,6 +303,131 @@ changes." data)))) +;;** Multi-file glossary support + +(defun or-scan-file-for-glossary-table (file) + "Scan FILE and return list of glossary entries from #+name: glossary table. +Returns list of plists with :label, :name, :description, :file. +Returns nil if no glossary table is found in FILE." + (when (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (org-mode) + (let ((entries '())) + (or (catch 'found + (org-element-map + (org-element-parse-buffer) + 'table + (lambda (el) + (when (string= "glossary" (org-element-property :name el)) + (goto-char (org-element-property :contents-begin el)) + (let ((table-data (nthcdr 2 (org-babel-read-table)))) + ;; Convert table rows to plists + (dolist (row table-data) + (when (and (listp row) (= 3 (length row))) + (push (list :label (nth 0 row) + :name (nth 1 row) + :description (nth 2 row) + :file file) + entries))) + (throw 'found (nreverse entries))))))) + entries))))) + + +(defun or-scan-file-for-acronym-table (file) + "Scan FILE and return list of acronym entries from #+name: acronyms table. +Returns list of plists with :label, :abbrv, :full, :file. +Returns nil if no acronym table is found in FILE." + (when (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (org-mode) + (let ((entries '())) + (or (catch 'found + (org-element-map + (org-element-parse-buffer) + 'table + (lambda (el) + (when (string= "acronyms" (org-element-property :name el)) + (goto-char (org-element-property :contents-begin el)) + (let ((table-data (nthcdr 2 (org-babel-read-table)))) + ;; Convert table rows to plists + (dolist (row table-data) + (when (and (listp row) (= 3 (length row))) + (push (list :label (nth 0 row) + :abbrv (nth 1 row) + :full (nth 2 row) + :file file) + entries))) + (throw 'found (nreverse entries))))))) + entries))))) + + +(defun or-parse-glossary-entry-multi-file (label) + "Find glossary LABEL in current file or included files. +Uses timestamp-based caching to avoid re-scanning unchanged files. +Returns plist with :label, :name, :description, :file." + (when (and (boundp 'org-ref-glossary-enable-multi-file) + org-ref-glossary-enable-multi-file + (boundp 'org-ref-glossary-file-cache) + (buffer-file-name)) + (cl-block or-parse-glossary-entry-multi-file + (let* ((current-file (buffer-file-name)) + (included-files (org-ref-get-included-files)) + (all-files (cons current-file included-files))) + + ;; Scan each file (with caching) + (dolist (file all-files) + ;; Scan if file changed OR if not in cache yet + (when (or (org-ref-file-changed-p file) + (not (gethash file org-ref-glossary-file-cache))) + ;; File changed or not cached, scan it + (let ((file-entries (or-scan-file-for-glossary-table file))) + (puthash file file-entries org-ref-glossary-file-cache) + (org-ref-mark-file-scanned file))) + + ;; Look for label in this file's cache + (let ((entries (gethash file org-ref-glossary-file-cache))) + (when entries + (let ((entry (cl-find label entries + :key (lambda (e) (plist-get e :label)) + :test 'string=))) + (when entry + (cl-return-from or-parse-glossary-entry-multi-file entry)))))))))) + + +(defun or-parse-acronym-entry-multi-file (label) + "Find acronym LABEL in current file or included files. +Uses timestamp-based caching to avoid re-scanning unchanged files. +Returns plist with :label, :abbrv, :full, :file." + (when (and (boundp 'org-ref-glossary-enable-multi-file) + org-ref-glossary-enable-multi-file + (boundp 'org-ref-acronym-file-cache) + (buffer-file-name)) + (cl-block or-parse-acronym-entry-multi-file + (let* ((current-file (buffer-file-name)) + (included-files (org-ref-get-included-files)) + (all-files (cons current-file included-files))) + + ;; Scan each file (with caching) + (dolist (file all-files) + ;; Scan if file changed OR if not in cache yet + (when (or (org-ref-file-changed-p file) + (not (gethash file org-ref-acronym-file-cache))) + ;; File changed or not cached, scan it + (let ((file-entries (or-scan-file-for-acronym-table file))) + (puthash file file-entries org-ref-acronym-file-cache) + (org-ref-mark-file-scanned file))) + + ;; Look for label in this file's cache + (let ((entries (gethash file org-ref-acronym-file-cache))) + (when entries + (let ((entry (cl-find label entries + :key (lambda (e) (plist-get e :label)) + :test 'string=))) + (when entry + (cl-return-from or-parse-acronym-entry-multi-file entry)))))))))) + ;;** Glossary links @@ -276,13 +436,22 @@ changes." set data on text with properties Set face property, and help-echo." (let ((data (or (or-parse-glossary-entry path) - (or-parse-acronym-entry path)))) + (or-parse-acronym-entry path) + ;; Try multi-file lookup if enabled and not found in current buffer + (or-parse-glossary-entry-multi-file path) + (or-parse-acronym-entry-multi-file path)))) (add-text-properties start end (list 'or-glossary data 'face (if data 'org-ref-glossary-face - 'font-lock-warning-face))))) + 'font-lock-warning-face) + ;; Suppress spell-checking with nospell property. + ;; For jinx users: add 'nospell to jinx-exclude-properties: + ;; (setq jinx-exclude-properties '((org-mode read-only nospell))) + ;; Or exclude by face using jinx-exclude-faces: + ;; (add-to-list 'jinx-exclude-faces 'org-ref-glossary-face) + 'nospell t)))) (defface org-ref-glossary-face `((t (:inherit org-link :foreground "Mediumpurple3"))) @@ -290,28 +459,53 @@ Set face property, and help-echo." (defun or-follow-glossary (entry) - "Goto beginning of the glossary ENTRY." + "Goto beginning of the glossary ENTRY. +If entry is in an included file, opens that file and navigates to the glossary table." (org-mark-ring-push) - (goto-char (plist-get (get-text-property (point) 'or-glossary) :position))) + (let* ((data (get-text-property (point) 'or-glossary)) + (file (plist-get data :file)) + (label (plist-get data :label)) + (position (plist-get data :position))) + (cond + ;; Entry in current buffer (has position) + (position + (goto-char position)) + ;; Entry in external file + (file + (find-file file) + (goto-char (point-min)) + (when (re-search-forward "^[ \t]*#\\+name:[ \t]+\\(glossary\\|acronyms\\)" nil t) + (when (re-search-forward (regexp-quote label) nil t) + (goto-char (line-beginning-position))))) + ;; Fallback: search in current buffer + (t + (goto-char (point-min)) + (when (re-search-forward "^[ \t]*#\\+name:[ \t]+\\(glossary\\|acronyms\\)" nil t) + (when (re-search-forward (regexp-quote label) nil t) + (goto-char (line-beginning-position)))))))) (defun or-glossary-tooltip (_window buffer position) "Return tooltip for the glossary entry. The entry is in WINDOW and OBJECT at POSITION. -Used in fontification." - (with-current-buffer buffer - (let* ((data (get-text-property position 'or-glossary)) - (name (or (plist-get data :name) - (plist-get data :abbrv))) - (description (or (plist-get data :description) - (plist-get data :full)))) - (format - "%s: %s" - name - (with-temp-buffer - (insert (concat description ".")) - (fill-paragraph) - (buffer-string)))))) +Used in fontification." + (when org-ref-glossary-show-tooltips + (with-current-buffer buffer + (let ((data (get-text-property position 'or-glossary))) + (if data + (let ((name (or (plist-get data :name) + (plist-get data :abbrv))) + (description (or (plist-get data :description) + (plist-get data :full)))) + (when (and name description) + (format "%s: %s" + name + (with-temp-buffer + (insert (concat description ".")) + (fill-paragraph) + (buffer-string))))) + ;; No data found - return helpful message or nil + "This is not defined in this file."))))) (defvar org-ref-glossary-gls-commands @@ -431,13 +625,13 @@ The plist maps to \newacronym{