From 60d1405ccfd6c2d1a333107556562d70f91b81c1 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Sun, 11 Dec 2022 13:15:17 +0100 Subject: [PATCH] add bibtex-completion Signed-off-by: Andreas Gerler --- .../bibtex-completion-autoloads.el | 53 + .../bibtex-completion-pkg.el | 2 + .../bibtex-completion.el | 1646 +++++++++++++++++ 3 files changed, 1701 insertions(+) create mode 100644 elpa/bibtex-completion-20220404.1608/bibtex-completion-autoloads.el create mode 100644 elpa/bibtex-completion-20220404.1608/bibtex-completion-pkg.el create mode 100644 elpa/bibtex-completion-20220404.1608/bibtex-completion.el diff --git a/elpa/bibtex-completion-20220404.1608/bibtex-completion-autoloads.el b/elpa/bibtex-completion-20220404.1608/bibtex-completion-autoloads.el new file mode 100644 index 00000000..8898ba92 --- /dev/null +++ b/elpa/bibtex-completion-20220404.1608/bibtex-completion-autoloads.el @@ -0,0 +1,53 @@ +;;; bibtex-completion-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*- +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "bibtex-completion" "bibtex-completion.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from bibtex-completion.el + (put 'bibtex-completion-bibliography 'safe-local-variable 'stringp) + +(put 'bibtex-completion-notes-global-mode 'globalized-minor-mode t) + +(defvar bibtex-completion-notes-global-mode nil "\ +Non-nil if Bibtex-Completion-Notes-Global mode is enabled. +See the `bibtex-completion-notes-global-mode' command +for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `bibtex-completion-notes-global-mode'.") + +(custom-autoload 'bibtex-completion-notes-global-mode "bibtex-completion" nil) + +(autoload 'bibtex-completion-notes-global-mode "bibtex-completion" "\ +Toggle Bibtex-Completion-Notes mode in all buffers. +With prefix ARG, enable Bibtex-Completion-Notes-Global mode if ARG is +positive; otherwise, disable it. + +If called from Lisp, toggle the mode if ARG is `toggle'. +Enable the mode if ARG is nil, omitted, or is a positive number. +Disable the mode if ARG is a negative number. + +Bibtex-Completion-Notes mode is enabled in all buffers where +`bibtex-completion-notes-mode' would do it. + +See `bibtex-completion-notes-mode' for more information on +Bibtex-Completion-Notes mode. + +\(fn &optional ARG)" t nil) + +(register-definition-prefixes "bibtex-completion" '("bibtex-completion-")) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; bibtex-completion-autoloads.el ends here diff --git a/elpa/bibtex-completion-20220404.1608/bibtex-completion-pkg.el b/elpa/bibtex-completion-20220404.1608/bibtex-completion-pkg.el new file mode 100644 index 00000000..1ed7e566 --- /dev/null +++ b/elpa/bibtex-completion-20220404.1608/bibtex-completion-pkg.el @@ -0,0 +1,2 @@ +;;; Generated package description from bibtex-completion.el -*- no-byte-compile: t -*- +(define-package "bibtex-completion" "20220404.1608" "A BibTeX backend for completion frameworks" '((parsebib "1.0") (s "1.9.0") (dash "2.6.0") (f "0.16.2") (cl-lib "0.5") (biblio "0.2") (emacs "26.1")) :commit "ce8c17690ddad73d01531084b282f221f8eb6669" :authors '(("Titus von der Malsburg" . "malsburg@posteo.de") ("Justin Burkett" . "justin@burkett.cc")) :maintainer '("Titus von der Malsburg" . "malsburg@posteo.de") :url "https://github.com/tmalsburg/helm-bibtex") diff --git a/elpa/bibtex-completion-20220404.1608/bibtex-completion.el b/elpa/bibtex-completion-20220404.1608/bibtex-completion.el new file mode 100644 index 00000000..59bcc349 --- /dev/null +++ b/elpa/bibtex-completion-20220404.1608/bibtex-completion.el @@ -0,0 +1,1646 @@ +;;; bibtex-completion.el --- A BibTeX backend for completion frameworks + +;; Author: Titus von der Malsburg +;; Justin Burkett +;; Maintainer: Titus von der Malsburg +;; URL: https://github.com/tmalsburg/helm-bibtex +;; Package-Version: 20220404.1608 +;; Package-Commit: ce8c17690ddad73d01531084b282f221f8eb6669 +;; Version: 1.0.0 +;; Package-Requires: ((parsebib "1.0") (s "1.9.0") (dash "2.6.0") (f "0.16.2") (cl-lib "0.5") (biblio "0.2") (emacs "26.1")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; A BibTeX backend for completion frameworks + +;; There are currently two fronends: helm-bibtex and ivy-bibtex. +;; +;; See the github page for details: +;; +;; https://github.com/tmalsburg/helm-bibtex + +;;; Code: + +(require 'browse-url) +(require 'parsebib) +(require 'cl-lib) +(require 'dash) +(require 's) +(require 'f) +(require 'biblio) +(require 'filenotify) +(require 'org-capture) + +;; Silence byte-compiler +(declare-function reftex-what-macro "reftex-parse") +(declare-function reftex-get-bibfile-list "reftex-cite") +(declare-function outline-show-all "outline") +(declare-function org-narrow-to-subtree "org") +(declare-function org-cycle-hide-drawers "org") +(declare-function org-find-property "org") +(declare-function org-show-entry "org") +(declare-function org-entry-get "org") +(declare-function org-element-parse-buffer "org-element") +(declare-function org-element-map "org-element") +(declare-function org-element-property "org-element") + +(defgroup bibtex-completion nil + "Helm plugin for searching entries in a BibTeX bibliography." + :group 'completion) + +(defcustom bibtex-completion-bibliography nil + "The BibTeX file or list of BibTeX files. +Org-bibtex users can also specify org mode bibliography files, in +which case it will be assumed that a BibTeX file exists with the +same name and extension bib instead of org. If the bib file has a +different name, use a cons cell `(\"orgfile.org\" . \"bibfile.bib\")' instead." + :group 'bibtex-completion + :type '(choice file (repeat file))) + +;;;###autoload (put 'bibtex-completion-bibliography 'safe-local-variable 'stringp) + +(defcustom bibtex-completion-library-path nil + "A directory or list of directories in which PDFs are stored. +Bibtex-completion assumes that the names of these PDFs are +composed of the BibTeX-key plus a \".pdf\" suffix." + :group 'bibtex-completion + :type '(choice directory (repeat directory))) + +(defcustom bibtex-completion-pdf-open-function 'find-file + "The function used for opening PDF files. +This can be an arbitrary function that takes one argument: the +path to the PDF file. The default is `find-file' which opens the +PDF in Emacs (either with docview or, if installed, the much +superior pdf-tools. When set to +`helm-open-file-with-default-tool', the systems default viewer +for PDFs is used." + :group 'bibtex-completion + :type 'function) + +(defcustom bibtex-completion-pdf-extension ".pdf" + "The extension of a BibTeX entry's \"PDF\" file. +This makes it possible to use another file type. It can also be a +list of file types, which are then tried sequentially until a +file is found. Beware that adding file types can reduce +performance for large bibliographies. This variable has no +effect if PDFs are referenced via the file field." + :group 'bibtex-completion + :type 'string) + +(defcustom bibtex-completion-find-additional-pdfs nil + "If non-nil, all files whose base name starts with the BibTeX key and ends with `bibtex-completion-pdf-extension' are considered as PDFs, not only \".\". +Note that for performance reasons, an entry is only marked as +having a PDF if \". (length (car it)) 0) entry-alist) + (cl-acons "" "" + (gethash (downcase crossref) entry-hash))))) + entry))))) + +(defun bibtex-completion-make-entry-hash (files reparsed-files) + "Return a hash table of all potentially cross-referenced bibliography entries in FILES, assuming that only those files in REPARSED-FILES were reparsed whereas the other files in FILES were up-to-date. +Only entries whose type belongs to +`bibtex-completion-cross-referenced-entry-types' are included in +the hash table." + (cl-loop + with entries = + (cl-loop + for file in files + for entries = (cddr (assoc file bibtex-completion-cache)) + if (member file reparsed-files) + ;; Entries are alists of \(FIELD . VALUE\) pairs. + append entries + ;; Entries are \(STRING . ALIST\) conses. + else + append (mapcar 'cdr entries)) + with ht = (make-hash-table :test #'equal :size (length entries)) + for entry in entries + for key = (bibtex-completion-get-value "=key=" entry) + if (member (downcase (bibtex-completion-get-value "=type=" entry)) + bibtex-completion-cross-referenced-entry-types) + do (puthash (downcase key) entry ht) + finally return ht)) + +(defun bibtex-completion-make-candidate (entry) + "Return a candidate for ENTRY." + (let* ((candidate (bibtex-completion-clean-string + (s-join " " (-map #'cdr entry)))) + (candidate (concat candidate " " (car (assoc "=has-pdf=" entry)))) + (candidate (concat candidate " " (car (assoc "=has-note=" entry))))) + (cons candidate entry))) + +(defun bibtex-completion-parse-bibliography (&optional ht-strings) + "Parse the BibTeX entries listed in the current buffer and return a list of entries in the order in which they appeared in the BibTeX file. +Also do some preprocessing of the entries. + +If HT-STRINGS is provided it is assumed to be a hash table." + (goto-char (point-min)) + (cl-loop + with fields = (append '("title" "crossref") + (-map (lambda (it) (if (symbolp it) (symbol-name it) it)) + bibtex-completion-additional-search-fields)) + for entry-type = (parsebib-find-next-item) + while entry-type + unless (member-ignore-case entry-type '("preamble" "string" "comment")) + collect (let* ((entry (parsebib-read-entry entry-type (point) ht-strings)) + (fields (append + (list (if (assoc-string "author" entry 'case-fold) + "author" + "editor") + (if (assoc-string "date" entry 'case-fold) + "date" + "year")) + fields))) + (-map (lambda (it) + (cons (downcase (car it)) (cdr it))) + (bibtex-completion-prepare-entry entry fields))))) + +(defun bibtex-completion-get-entry (entry-key) + "Given a BibTeX key this function scans all bibliographies listed in `bibtex-completion-bibliography' and returns an alist of the record with that key. +Fields from crossreferenced entries are appended to the requested entry." + (let* ((entry (bibtex-completion-get-entry1 entry-key)) + (crossref (bibtex-completion-get-value "crossref" entry)) + (crossref (when crossref (bibtex-completion-get-entry1 crossref)))) + (bibtex-completion-remove-duplicated-fields (append entry crossref)))) + +(defun bibtex-completion-get-entry1 (entry-key &optional do-not-find-pdf) + (let ((bib (bibtex-completion-normalize-bibliography 'bibtex))) + (with-temp-buffer + (mapc #'insert-file-contents bib) + (goto-char (point-min)) + (if (re-search-forward (concat "^[ \t]*@\\(" parsebib--bibtex-identifier + "\\)[[:space:]]*[\(\{][[:space:]]*" + (regexp-quote entry-key) "[[:space:]]*,") + nil t) + (let ((entry-type (match-string 1))) + (reverse (bibtex-completion-prepare-entry + (parsebib-read-entry entry-type (point) bibtex-completion-string-hash-table) nil do-not-find-pdf))) + (progn + (display-warning :warning (concat "Bibtex-completion couldn't find entry with key \"" entry-key "\".")) + nil))))) + +(defun bibtex-completion-find-pdf-in-field (key-or-entry) + "Return the path of the PDF specified in the field `bibtex-completion-pdf-field' if that file exists. +Returns nil if no file is specified, or if the specified file +does not exist, or if `bibtex-completion-pdf-field' is nil." + (when bibtex-completion-pdf-field + (let* ((entry (if (stringp key-or-entry) + (bibtex-completion-get-entry1 key-or-entry t) + key-or-entry)) + (value (bibtex-completion-get-value bibtex-completion-pdf-field entry))) + (cond + ((not value) nil) ; Field not defined. + ((f-file? value) (list value)) ; A bare full path was found. + ((-any 'f-file? (--map (f-join it (f-filename value)) (-flatten bibtex-completion-library-path))) (-filter 'f-file? (--map (f-join it (f-filename value)) (-flatten bibtex-completion-library-path)))) + (t ; Zotero/Mendeley/JabRef/Calibre format: + (let ((value (replace-regexp-in-string "\\([^\\]\\)[;,]" "\\1\^^" value))) + (cl-loop ; Looping over the files: + for record in (s-split "\^^" value) + ; Replace unescaped colons by field separator: + for record = (replace-regexp-in-string "\\([^\\]\\|^\\):" "\\1\^_" record) + ; Unescape stuff: + for record = (replace-regexp-in-string "\\\\\\(.\\)" "\\1" record) + ; Now we can safely split: + for record = (s-split "\^_" record) + for file-name = (nth 0 record) + for path = (or (nth 1 record) "") + for paths = (if (s-match "^[A-Z]:" path) + (list path) ; Absolute Windows path + ; Something else: + (append + (list + path + file-name + (f-join (f-root) path) ; Mendeley #105 + (f-join (f-root) path file-name)) ; Mendeley #105 + (--map (f-join it path) + (-flatten bibtex-completion-library-path)) ; Jabref #100 + (--map (f-join it path file-name) + (-flatten bibtex-completion-library-path)))) ; Jabref #100 + for result = (-first (lambda (path) + (if (and (not (s-blank-str? path)) + (f-exists? path)) + path nil)) paths) + if result collect result))))))) + + +(defun bibtex-completion-find-pdf-in-library (key-or-entry &optional find-additional) + "Searches the directories in `bibtex-completion-library-path' for a PDF whose name is composed of the BibTeX key plus `bibtex-completion-pdf-extension'. +The path of the first matching PDF is returned. + +If FIND-ADDITIONAL is non-nil, the paths of all PDFs whose name +starts with the BibTeX key and ends with +`bibtex-completion-pdf-extension' are returned instead." + (let* ((key (if (stringp key-or-entry) + key-or-entry + (bibtex-completion-get-value "=key=" key-or-entry))) + (main-pdf (cl-loop + for dir in (-flatten bibtex-completion-library-path) + append (cl-loop + for ext in (-flatten bibtex-completion-pdf-extension) + collect (f-join dir (s-concat key ext)))))) + (if find-additional + (sort ; move main pdf on top of the list if needed + (cl-loop + for dir in (-flatten bibtex-completion-library-path) + append (directory-files dir t + (s-concat "^" (regexp-quote key) + ".*\\(" + (mapconcat 'regexp-quote + (-flatten bibtex-completion-pdf-extension) + "\\|") + "\\)$"))) + (lambda (x y) + (and (member x main-pdf) + (not (member y main-pdf))))) + (-flatten (-first 'f-file? main-pdf))))) + +(defun bibtex-completion-find-pdf (key-or-entry &optional find-additional) + "Return the path of the PDF associated with the specified entry KEY-OR-ENTRY. +This is either the path(s) specified in the field +`bibtex-completion-pdf-field' or, if that does not exist, the +first PDF in any of the directories in +`bibtex-completion-library-path' whose name is composed of the +the BibTeX key plus `bibtex-completion-pdf-extension' (or if +FIND-ADDITIONAL is non-nil, all PDFs in +`bibtex-completion-library-path' whose name starts with the +BibTeX key and ends with `bibtex-completion-pdf-extension'). +Returns nil if no PDF is found." + (or (bibtex-completion-find-pdf-in-field key-or-entry) + (bibtex-completion-find-pdf-in-library key-or-entry find-additional))) + +(defun bibtex-completion-find-note-multiple-files (entry-key) + "Find note file associated with entry ENTRY-KEY in the default directory. +The default directory is `bibtex-completion-notes-path'. If the +note file doesn’t exist, return nil." + (and bibtex-completion-notes-path + (f-directory? bibtex-completion-notes-path) + (f-file? (f-join bibtex-completion-notes-path + (s-concat entry-key + bibtex-completion-notes-extension))))) + +(defun bibtex-completion-find-note-one-file (entry-key) + "Find notes associated with entry ENTRY-KEY in the single notes file. +The single notes file is the one specified in +`bibtex-completion-notes-path'. If no note exists, return nil." + (and bibtex-completion-notes-path + (f-file? bibtex-completion-notes-path) + (member entry-key bibtex-completion-cached-notes-keys))) + +;; This defvar allows other packages like org-roam-bibtex to customize +;; the back-end for storing notes. +(defvar bibtex-completion-find-note-functions + (list #'bibtex-completion-find-note-multiple-files + #'bibtex-completion-find-note-one-file) + "List of functions to use to find note files. +The functions should accept one argument: the key of the BibTeX +entry and return non-nil if notes exist for that entry.") + +(defun bibtex-completion-prepare-entry (entry &optional fields do-not-find-pdf) + "Prepare ENTRY for display. +ENTRY is an alist representing an entry as returned by +`parsebib-read-entry'. All the fields not in FIELDS are removed +from ENTRY, with the exception of the \"=type=\" and \"=key=\" +fields. If FIELDS is empty, all fields are kept. Also add a +=has-pdf= and/or =has-note= field, if they exist for ENTRY. If +DO-NOT-FIND-PDF is non-nil, this function does not attempt to +find a PDF file." + (when entry ; entry may be nil, in which case just return nil + (let* ((fields (when fields (append fields (list "=type=" "=key=" "=has-pdf=" "=has-note=")))) + ; Check for PDF: + (entry (if (and (not do-not-find-pdf) (bibtex-completion-find-pdf entry)) + (cons (cons "=has-pdf=" bibtex-completion-pdf-symbol) entry) + entry)) + (entry-key (cdr (assoc "=key=" entry))) + ; Check for notes: + (entry (if (cl-some #'identity + (mapcar (lambda (fn) + (funcall fn entry-key)) + bibtex-completion-find-note-functions)) + (cons (cons "=has-note=" bibtex-completion-notes-symbol) entry) + entry)) + ; Remove unwanted fields: + (entry (if fields + (--filter (member-ignore-case (car it) fields) entry) + entry))) + ;; Normalize case of entry type: + (setcdr (assoc "=type=" entry) (downcase (cdr (assoc "=type=" entry)))) + ;; Remove duplicated fields: + (bibtex-completion-remove-duplicated-fields entry)))) + +(defun bibtex-completion-remove-duplicated-fields (entry) + "Remove duplicated fields from ENTRY." + (cl-remove-duplicates entry + :test (lambda (x y) (string= (s-downcase x) (s-downcase y))) + :key 'car :from-end t)) + + +(defun bibtex-completion-format-entry (entry width) + "Formats a BibTeX ENTRY for display in results list. +WIDTH is the width of the results list. The display format is +governed by the variable `bibtex-completion-display-formats'." + (let* ((format + (or (assoc-string (bibtex-completion-get-value "=type=" entry) + bibtex-completion-display-formats-internal + 'case-fold) + (assoc t bibtex-completion-display-formats-internal))) + (format-string (cadr format))) + (s-format + format-string + (lambda (field) + (let* ((field (split-string field ":")) + (field-name (car field)) + (field-width (cadr field)) + (field-value (bibtex-completion-get-value field-name entry))) + (when (and (string= field-name "author") + (not field-value)) + (setq field-value (bibtex-completion-get-value "editor" entry))) + (when (and (string= field-name "year") + (not field-value)) + (setq field-value (car (split-string (bibtex-completion-get-value "date" entry "") "-")))) + (setq field-value (bibtex-completion-clean-string (or field-value " "))) + (when (member field-name '("author" "editor")) + (setq field-value (bibtex-completion-shorten-authors field-value))) + (if (not field-width) + field-value + (setq field-width (string-to-number field-width)) + (truncate-string-to-width + field-value + (if (> field-width 0) + field-width + (- width (cddr format))) + 0 ?\s))))))) + + +(defun bibtex-completion-clean-string (s) + "Remove quoting and superfluous white space from BibTeX field value in S." + (if s (replace-regexp-in-string "[\n\t ]+" " " + (replace-regexp-in-string "[\"{}]+" "" s)) + nil)) + +(defun bibtex-completion-shorten-authors (authors) + "Return a comma-separated list of the surnames in AUTHORS." + (if authors + (cl-loop for a in (s-split " and " authors) + for p = (s-split "," a t) + for sep = "" then ", " + concat sep + if (eq 1 (length p)) + concat (-last-item (s-split " +" (car p) t)) + else + concat (car p)) + nil)) + + +(defun bibtex-completion-open-pdf (keys &optional fallback-action) + "Open the PDFs associated with the marked entries using the function specified in `bibtex-completion-pdf-open-function'. +If multiple PDFs are found for an entry, ask for the one to open +using `completion-read'. If FALLBACK-ACTION is non-nil, it is +called in case no PDF is found." + (dolist (key keys) + (let ((pdf (bibtex-completion-find-pdf key bibtex-completion-find-additional-pdfs))) + (cond + ((> (length pdf) 1) + (let* ((pdf (f-uniquify-alist pdf)) + (choice (completing-read "File to open: " (mapcar 'cdr pdf) nil t)) + (file (car (rassoc choice pdf)))) + (funcall bibtex-completion-pdf-open-function file))) + (pdf + (funcall bibtex-completion-pdf-open-function (car pdf))) + (fallback-action + (funcall fallback-action (list key))) + (t + (message "No PDF(s) found for this entry: %s" + key)))))) + +(defun bibtex-completion-open-url-or-doi (keys) + "Open the URL or DOI associated with entries in KEYS in a browser." + (dolist (key keys) + (let* ((entry (bibtex-completion-get-entry key)) + (url (bibtex-completion-get-value "url" entry)) + (doi (bibtex-completion-get-value "doi" entry)) + (browse-url-browser-function + (or bibtex-completion-browser-function + browse-url-browser-function))) + (if url + (browse-url url) + (if doi (browse-url + (s-concat "http://dx.doi.org/" doi)) + (message "No URL or DOI found for this entry: %s" + key)))))) + +(defun bibtex-completion-open-any (keys) + "Open the PDFs associated with the marked entries using the function specified in `bibtex-completion-pdf-open-function'. +If multiple PDFs are found for an entry, ask for the one to open +using `completion-read'. If no PDF is found, try to open a URL +or DOI in the browser instead." + (bibtex-completion-open-pdf keys 'bibtex-completion-open-url-or-doi)) + +(defun bibtex-completion-format-citation-default (keys) + "Default formatter for keys, separates multiple keys in KEYS with commas." + (s-join ", " keys)) + +(defvar bibtex-completion-cite-command-history nil + "History list for LaTeX citation commands.") + +(defun bibtex-completion-format-citation-cite (keys) + "Formatter for LaTeX citation commands. +Prompts for the command and for arguments if the commands can +take any. If point is inside or just after a citation command, +only adds KEYS to it." + (let (macro) + (cond + ((and (require 'reftex-parse nil t) + (setq macro (reftex-what-macro 1)) + (stringp (car macro)) + (string-match "\\`\\\\cite\\|cite\\'" (car macro))) + ;; We are inside a cite macro. Insert key at point, with appropriate delimiters. + (delete-horizontal-space) + (concat (pcase (preceding-char) + (?\{ "") + (?, " ") + (_ ", ")) + (s-join ", " keys) + (if (member (following-char) '(?\} ?,)) + "" + ", "))) + ((and (equal (preceding-char) ?\}) + (require 'reftex-parse nil t) + (save-excursion + (forward-char -1) + (setq macro (reftex-what-macro 1))) + (stringp (car macro)) + (string-match "\\`\\\\cite\\|cite\\'" (car macro))) + ;; We are right after a cite macro. Append key and leave point at the end. + (delete-char -1) + (delete-horizontal-space t) + (concat (pcase (preceding-char) + (?\{ "") + (?, " ") + (_ ", ")) + (s-join ", " keys) + "}")) + (t + ;; We are not inside or right after a cite macro. Insert a full citation. + (let* ((initial (when bibtex-completion-cite-default-as-initial-input + bibtex-completion-cite-default-command)) + (default (unless bibtex-completion-cite-default-as-initial-input + bibtex-completion-cite-default-command)) + (default-info (if default (format " (default \"%s\")" default) "")) + (cite-command (completing-read + (format "Cite command%s: " default-info) + bibtex-completion-cite-commands nil nil initial + 'bibtex-completion-cite-command-history default nil))) + (if (member cite-command '("nocite" "supercite")) ; These don't want arguments. + (format "\\%s{%s}" cite-command (s-join ", " keys)) + (let ((prenote (if bibtex-completion-cite-prompt-for-optional-arguments + (read-from-minibuffer "Prenote: ") + "")) + (postnote (if bibtex-completion-cite-prompt-for-optional-arguments + (read-from-minibuffer "Postnote: ") + ""))) + (cond ((not (string= "" prenote)) + (format "\\%s[%s][%s]{%s}" cite-command prenote postnote (s-join ", " keys))) + ((not (string= "" postnote)) + (format "\\%s[%s]{%s}" cite-command postnote (s-join ", " keys))) + (t + (format "\\%s{%s}" cite-command (s-join ", " keys))))))))))) + +(defun bibtex-completion-format-citation-pandoc-citeproc (keys) + "Format pandoc-citeproc citations for the entries in KEYS." + (let* ((prenote (if bibtex-completion-cite-prompt-for-optional-arguments (read-from-minibuffer "Prenote: ") "")) + (postnote (if bibtex-completion-cite-prompt-for-optional-arguments (read-from-minibuffer "Postnote: ") "")) + (prenote (if (string= "" prenote) "" (concat prenote " "))) + (postnote (if (string= "" postnote) "" (concat ", " postnote)))) + (format "[%s%s%s]" prenote (s-join "; " (--map (concat "@" it) keys)) postnote))) + +(defun bibtex-completion-format-citation-ebib (keys) + "Format ebib references for keys in KEYS." + (s-join ", " + (--map (format "ebib:%s" it) keys))) + +(defun bibtex-completion-format-citation-sphinxcontrib-bibtex (keys) + "Format sphinxcontrib-bibtex references for keys in KEYS." + (format ":cite:`%s`" (s-join "," keys))) + +(defun bibtex-completion-format-citation-org-link-to-PDF (keys) + "Format org-links to PDFs associated with entries in KEYS. +Uses first matching PDF if several are available. Entries for +which no PDF is available are omitted." + (s-join ", " (cl-loop + for key in keys + for pdfs = (bibtex-completion-find-pdf key bibtex-completion-find-additional-pdfs) + append (with-no-warnings (--map (org-make-link-string it key) pdfs))))) + +(defun bibtex-completion-format-citation-org-cite (keys) + "Format org-links using Org mode's own cite syntax." + (format "[cite:%s]" + (s-join ";" + (--map (format "@%s" it) keys)))) + +(defun bibtex-completion-format-citation-org-apa-link-to-PDF (keys) + "Format org-links to PDF for entries in KEYS. +Link text loosely follows APA format. Uses first matching PDF if +several are available." + (s-join ", " (cl-loop + for key in keys + for entry = (bibtex-completion-get-entry key) + for author = (bibtex-completion-shorten-authors + (or (bibtex-completion-get-value "author" entry) + (bibtex-completion-get-value "editor" entry))) + for year = (or (bibtex-completion-get-value "year" entry) + (car (split-string (bibtex-completion-get-value "date" entry "") "-"))) + for pdf = (car (bibtex-completion-find-pdf key)) + if pdf + collect (with-no-warnings (org-make-link-string pdf (format "%s (%s)" author year))) + else + collect (format "%s (%s)" author year)))) + +;; When you want to create a todo list about reading, I think using +;; PDF's title is more intuitive. +(defun bibtex-completion-format-citation-org-title-link-to-PDF (keys) + "Formatter org-links to PDFs associated with entries in KEYS. +Link text follows file title format. Uses first matching PDF if +several are available." + (s-join ", " (cl-loop + for key in keys + for entry = (bibtex-completion-get-entry key) + for title = (bibtex-completion-apa-get-value "title" entry) + for pdf = (or (car (bibtex-completion-find-pdf key)) + (bibtex-completion-get-value "url" entry)) + if pdf + collect (with-no-warnings (org-make-link-string pdf title)) + else + collect (format "%s" title)))) + +(defun bibtex-completion-insert-citation (keys) + "Insert citations for entries in KEYS at point. +The format depends on +`bibtex-completion-format-citation-functions'." + (let ((format-function + (cdr (or (assoc major-mode bibtex-completion-format-citation-functions) + (assoc 'default bibtex-completion-format-citation-functions))))) + (insert + (funcall format-function keys)))) + +(defun bibtex-completion-insert-reference (keys) + "Insert references for entries in KEYS." + (let* ((refs (--map + (s-word-wrap fill-column + (concat "\n- " (bibtex-completion-apa-format-reference it))) + keys))) + (insert "\n" (s-join "\n" refs) "\n"))) + +(defun bibtex-completion-apa-format-reference (key) + "Return a plain text reference in APA format for the publication specified by KEY." + (let* + ((entry (bibtex-completion-get-entry key)) + (ref (pcase (downcase (bibtex-completion-get-value "=type=" entry)) + ("article" + (s-format + "${author} (${year}). ${title}. ${journal}, ${volume}(${number}), ${pages}.${doi}" + 'bibtex-completion-apa-get-value entry)) + ("inproceedings" + (s-format + "${author} (${year}). ${title}. In ${editor}, ${booktitle} (pp. ${pages}). ${address}: ${publisher}." + 'bibtex-completion-apa-get-value entry)) + ("book" + (s-format + "${author} (${year}). ${title}. ${address}: ${publisher}." + 'bibtex-completion-apa-get-value entry)) + ("phdthesis" + (s-format + "${author} (${year}). ${title} (Doctoral dissertation). ${school}, ${address}." + 'bibtex-completion-apa-get-value entry)) + ("inbook" + (s-format + "${author} (${year}). ${chapter}. In ${editor} (Eds.), ${title} (pp. ${pages}). ${address}: ${publisher}." + 'bibtex-completion-apa-get-value entry)) + ("incollection" + (s-format + "${author} (${year}). ${title}. In ${editor} (Eds.), ${booktitle} (pp. ${pages}). ${address}: ${publisher}." + 'bibtex-completion-apa-get-value entry)) + ("proceedings" + (s-format + "${editor} (Eds.). (${year}). ${booktitle}. ${address}: ${publisher}." + 'bibtex-completion-apa-get-value entry)) + ("unpublished" + (s-format + "${author} (${year}). ${title}. Unpublished manuscript." + 'bibtex-completion-apa-get-value entry)) + (_ + (s-format + "${author} (${year}). ${title}." + 'bibtex-completion-apa-get-value entry))))) + (replace-regexp-in-string "\\([.?!]\\)\\." "\\1" ref))) ; Avoid sequences of punctuation marks. + +(defun bibtex-completion-apa-get-value (field entry &optional default) + "Return FIELD or ENTRY formatted following the APA guidelines. +Return DEFAULT if FIELD is not present in ENTRY. Return empty +string if FIELD is not present in ENTRY and DEFAULT is nil." + (or + (pcase field + ;; Virtual fields: + ("author-or-editor" + ;; Avoid if-let and when-let because they're not working reliably + ;; in all versions of Emacs that we currently support: + (if-let ((value (bibtex-completion-get-value "author" entry))) + (bibtex-completion-apa-format-authors value) + (when-let ((value (bibtex-completion-get-value "editor" entry))) + (bibtex-completion-apa-format-editors value)))) + ("author-or-editor-abbrev" + (if-let ((value (bibtex-completion-get-value "author" entry))) + (bibtex-completion-apa-format-authors-abbrev value) + (when-let ((value (bibtex-completion-get-value "editor" entry))) + (bibtex-completion-apa-format-editors-abbrev value)))) + ("author-abbrev" + (when-let ((value (bibtex-completion-get-value "author" entry))) + (bibtex-completion-apa-format-authors-abbrev value))) + ("editor-abbrev" + (when-let ((value (bibtex-completion-get-value "editor" entry))) + (bibtex-completion-apa-format-editors-abbrev value))) + (_ + ;; Real fields: + (let ((value (bibtex-completion-get-value field entry))) + (if value + (pcase field + ;; https://owl.english.purdue.edu/owl/resource/560/06/ + ("author" (bibtex-completion-apa-format-authors value)) + ("editor" (bibtex-completion-apa-format-editors value)) + ;; When referring to books, chapters, articles, or Web pages, + ;; capitalize only the first letter of the first word of a + ;; title and subtitle, the first word after a colon or a dash + ;; in the title, and proper nouns. Do not capitalize the first + ;; letter of the second word in a hyphenated compound word. + ("title" (replace-regexp-in-string ; remove braces + "[{}]" + "" + (replace-regexp-in-string ; remove macros + "\\\\[[:alpha:]]+{" + "" + (replace-regexp-in-string ; upcase initial letter + "^[[:alpha:]]" + 'upcase + (replace-regexp-in-string ; preserve stuff in braces from being downcased + "\\(^[^{]*{\\)\\|\\(}[^{]*{\\)\\|\\(}.*$\\)\\|\\(^[^{}]*$\\)" + (lambda (x) (downcase (s-replace "\\" "\\\\" x))) + value))))) + ("booktitle" value) + ;; Maintain the punctuation and capitalization that is used by + ;; the journal in its title. + ("pages" (s-join "–" (s-split "[^0-9]+" value t))) + ("doi" (s-concat " http://dx.doi.org/" value)) + ("year" (or value + (car (split-string (bibtex-completion-get-value "date" entry "") "-")))) + (_ value)))))) + default "")) + +(defun bibtex-completion-apa-format-authors (value &optional abbrev) + "Format author list in VALUE in APA style. +When ABBREV is non-nil, format in abbreviated APA style instead." + (cl-loop for a in (s-split " and " value t) + if (s-index-of "{" a) + collect + (replace-regexp-in-string "[{}]" "" a) + into authors + else if (s-index-of "," a) + collect + (let ((p (s-split " *, *" a t))) + (concat + (car p) ", " + (s-join " " (-map (lambda (it) (concat (s-left 1 it) ".")) + (s-split " " (cadr p)))))) + into authors + else + collect + (let ((p (s-split " " a t))) + (concat + (-last-item p) ", " + (s-join " " (-map (lambda (it) (concat (s-left 1 it) ".")) + (-butlast p))))) + into authors + finally return + (let ((l (length authors))) + (cond + ((= l 1) (car authors)) + ((and abbrev (= l 2)) + (concat (s-join " & " authors))) + (abbrev + (format "%s et al." (car authors))) + ((< l 8) (concat (s-join ", " (-butlast authors)) + ", & " (-last-item authors))) + (t (concat (s-join ", " (-slice authors 0 7)) ", …")))))) + +(defun bibtex-completion-apa-format-authors-abbrev (value) + "Format author list in VALUE in abbreviated APA style." + (bibtex-completion-apa-format-authors value t)) + +(defun bibtex-completion-apa-format-editors (value &optional abbrev) + "Format editors in VALUE in APA style. +When ABBREV is non-nil, format in abbreviated APA style instead." + (cl-loop for a in (s-split " and " value t) + if (s-index-of "," a) + collect + (let ((p (s-split " *, *" a t))) + (concat + (s-join " " (-map (lambda (it) (concat (s-left 1 it) ".")) + (s-split " " (cadr p)))) + " " (car p))) + into editors + else + collect + (let ((p (s-split " " a t))) + (concat + (s-join " " (-map (lambda (it) (concat (s-left 1 it) ".")) + (-butlast p))) + " " (-last-item p))) + into editors + finally return + (let ((l (length editors))) + (cond + ((= l 1) (car editors)) + ((and abbrev (= l 2)) + (concat (s-join " & " editors))) + (abbrev + (format "%s et al." (car editors))) + ((< l 8) (concat (s-join ", " (-butlast editors)) + ", & " (-last-item editors))) + (t (concat (s-join ", " (-slice editors 0 7)) ", …")))))) + +(defun bibtex-completion-apa-format-editors-abbrev (value) + "Format editor list in VALUE in abbreviated APA style." + (bibtex-completion-apa-format-editors value t)) + +(defun bibtex-completion-get-value (field entry &optional default) + "Return the value for FIELD in ENTRY or DEFAULT if the value is not defined. +Surrounding curly braces are stripped." + (let ((value (cdr (assoc-string field entry 'case-fold)))) + (if value + (replace-regexp-in-string + "\\(^[[:space:]]*[\"{][[:space:]]*\\)\\|\\([[:space:]]*[\"}][[:space:]]*$\\)" + "" + ;; Collapse whitespaces when the content is not a path: + (if (equal bibtex-completion-pdf-field field) + value + (s-collapse-whitespace value))) + default))) + +(defun bibtex-completion-insert-key (keys) + "Insert BibTeX KEYS at point." + (insert + (funcall 'bibtex-completion-format-citation-default keys))) + +(defun bibtex-completion-insert-bibtex (keys) + "Insert BibTeX entries for entries in KEYS at point." + (insert (s-join "\n" (--map (bibtex-completion-make-bibtex it) keys)))) + +(defun bibtex-completion-make-bibtex (key) + "Create a self-contained BibTeX entry KEY. +Self-contained means that cross-referenced entries are merged." + (let* ((entry (bibtex-completion-get-entry key)) + (entry-type (bibtex-completion-get-value "=type=" entry))) + (format "@%s{%s,\n%s}\n" + entry-type key + (cl-loop + for field in entry + for name = (car field) + for value = (cdr field) + unless (member name + (append (-map (lambda (it) (if (symbolp it) (symbol-name it) it)) + bibtex-completion-no-export-fields) + '("=type=" "=key=" "=has-pdf=" "=has-note=" "crossref"))) + concat + (format " %s = {%s},\n" name value))))) + +(defun bibtex-completion-add-PDF-attachment (keys) + "Attach the PDFs of the entries with the given KEYS where available." + (dolist (key keys) + (let ((pdf (bibtex-completion-find-pdf key bibtex-completion-find-additional-pdfs))) + (if pdf + (mapc 'mml-attach-file pdf) + (message "No PDF(s) found for this entry: %s" + key))))) + +(define-minor-mode bibtex-completion-notes-mode + "Minor mode for managing notes." + :keymap (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-c") 'bibtex-completion-exit-notes-buffer) + (define-key map (kbd "C-c C-w") 'org-refile) + map) + (setq-local + header-line-format + (substitute-command-keys + " Finish \\[bibtex-completion-exit-notes-buffer], refile \\[org-refile]"))) + +;; Define global minor mode. This is needed to the toggle minor mode. +;;;###autoload +(define-globalized-minor-mode bibtex-completion-notes-global-mode bibtex-completion-notes-mode bibtex-completion-notes-mode) + +(defun bibtex-completion-exit-notes-buffer () + "Exit notes buffer and delete its window. +This will also disable `bibtex-completion-notes-mode' and remove +the header line." + (interactive) + (widen) + (bibtex-completion-notes-global-mode -1) + (setq-local + header-line-format nil) + (save-buffer) + (let ((window (get-buffer-window (get-file-buffer bibtex-completion-notes-path)))) + (if (and window (not (one-window-p window))) + (delete-window window) + (switch-to-buffer (other-buffer))))) + +(defun bibtex-completion-fill-template (entry template) + "Fill TEMPLATE according to info from ENTRY. + +First, the BibTeX fields are expanded (e.g. ${field-name}). +Then, the `org-capture' %-escapes are replaced with their values +according to `org-capture-templates'." + (let ((bibtex-exp (s-format template + 'bibtex-completion-apa-get-value + entry))) + ;; Delete trailing newline inserted by `org-capture-fill-template' + (substring + (->> bibtex-exp + ;; Escape newlines to prevent `org-capture-fill-template' from + ;; gobbling them + (replace-regexp-in-string "\n" "\\\\n") + (org-capture-fill-template) + ;; Restore newlines + (replace-regexp-in-string "\\\\n" "\n")) + 0 -1))) + +;; The purpose of this defvar is to allow other packages like +;; org-roam-bibtex to customize the back-end used for notes. +(defvar bibtex-completion-edit-notes-function + #'bibtex-completion-edit-notes-default + "Function used to edit notes. +The function should accept one argument, a list of BibTeX keys.") + +;; TODO Split this function into two, one for one file per note and +;; the other for one file for all notes. +(defun bibtex-completion-edit-notes-default (keys) + "Open the notes associated with the entries in KEYS. +Creates new notes where none exist yet." + (dolist (key keys) + (let* ((entry (bibtex-completion-get-entry key)) + (year (or (bibtex-completion-get-value "year" entry) + (car (split-string (bibtex-completion-get-value "date" entry "") "-")))) + (entry (push (cons "year" year) entry))) + (if (and bibtex-completion-notes-path + (f-directory? bibtex-completion-notes-path)) + ; One notes file per publication: + (let* ((path (f-join bibtex-completion-notes-path + (s-concat key bibtex-completion-notes-extension)))) + (find-file path) + (unless (f-exists? path) + ;; First expand BibTeX variables, then org-capture template vars: + (insert (bibtex-completion-fill-template + entry + bibtex-completion-notes-template-multiple-files)))) + ; One file for all notes: + (unless (and buffer-file-name + (f-same? bibtex-completion-notes-path buffer-file-name)) + (find-file-other-window bibtex-completion-notes-path)) + (widen) + (outline-show-all) + (goto-char (point-min)) + (if (re-search-forward (format bibtex-completion-notes-key-pattern (regexp-quote key)) nil t) + ; Existing entry found: + (when (eq major-mode 'org-mode) + (org-narrow-to-subtree) + (re-search-backward "^\*+ " nil t) + (org-cycle-hide-drawers nil) + (bibtex-completion-notes-mode 1)) + ; Create a new entry: + (goto-char (point-max)) + (save-excursion (insert (bibtex-completion-fill-template + entry + bibtex-completion-notes-template-one-file))) + (re-search-forward "^*+ " nil t)) + (when (eq major-mode 'org-mode) + (org-narrow-to-subtree) + (re-search-backward "^\*+ " nil t) + (org-cycle-hide-drawers nil) + (goto-char (point-max)) + (bibtex-completion-notes-mode 1)) + ;; Move point to ‘%?’ if it’s included in the pattern + (when (save-excursion + (progn (goto-char (point-min)) + (re-search-forward "%\\?" nil t))) + (let ((beginning (match-beginning 0)) + (end (match-end 0))) + (delete-region beginning end) + (goto-char beginning))))))) + +(defun bibtex-completion-edit-notes (keys) + "Open the notes associated with KEYS using `bibtex-completion-edit-notes-function'." + (funcall bibtex-completion-edit-notes-function keys)) + +(defun bibtex-completion-show-entry (keys) + "Show the first entry in KEYS in the relevant BibTeX file." + (catch 'break + (dolist (bib-file (bibtex-completion-normalize-bibliography 'main)) + (let ((key (car keys)) + (buf (or (get-file-buffer bib-file) + (find-buffer-visiting bib-file)))) + (find-file bib-file) + (widen) + (if (eq major-mode 'org-mode) + (let* ((prop (if (boundp 'org-bibtex-key-property) + org-bibtex-key-property + "CUSTOM_ID")) + (match (org-find-property prop key))) + (when match + (goto-char match) + (org-show-entry) + (throw 'break t))) + (goto-char (point-min)) + (when (re-search-forward + (concat "^@\\(" parsebib--bibtex-identifier + "\\)[[:space:]]*[\(\{][[:space:]]*" + (regexp-quote key) "[[:space:]]*,") nil t) + (throw 'break t))) + (unless buf + (kill-buffer)))))) + +(defun bibtex-completion-add-pdf-to-library (keys) + "Add a PDF to the library for the first entry in KEYS. +The PDF can be added either from an open buffer, a file, or a +URL." + (let* ((key (car keys)) + (source (char-to-string + (read-char-choice "Add pdf from [b]uffer, [f]ile, or [u]rl? " '(?b ?f ?u)))) + (buffer (when (string= source "b") + (read-buffer-to-switch "Add pdf buffer: "))) + (file (when (string= source "f") + (expand-file-name (read-file-name "Add pdf file: " nil nil t)))) + (url (when (string= source "u") + (read-string "Add pdf URL: "))) + (path (-flatten (list bibtex-completion-library-path))) + (path (if (cdr path) + (completing-read "Add pdf to: " path nil t) + (car path))) + (pdf (expand-file-name (completing-read "Rename pdf to: " + (--map (s-concat key it) + (-flatten bibtex-completion-pdf-extension)) + nil nil key) + path))) + (cond + (buffer + (with-current-buffer buffer + (write-file pdf t))) + (file + (copy-file file pdf 1)) + (url + (url-copy-file url pdf 1))))) + +(defun bibtex-completion-fallback-action (url-or-function search-expression) + "Execute fallback option. +Could consist of opening an URL or executing a function, +depending of type of URL-OR-FUNCTION (`stringp' or `function'). +If string, SEARCH-EXPRESSION will be inserted at %s in string. +If function, it will be called with SEARCH-EXPRESSION as +argument." + (let ((browse-url-browser-function + (or bibtex-completion-browser-function + browse-url-browser-function))) + (cond + ((stringp url-or-function) + (browse-url (format url-or-function (url-hexify-string search-expression)))) + ((functionp url-or-function) + (funcall url-or-function search-expression)) + (t (error "Don't know how to interpret this: %s" url-or-function))))) + +(defun bibtex-completion-fallback-candidates () + "Compile list of fallback options. +These consist of the online resources defined in +`bibtex-completion-fallback-options' plus one entry for each +bibliography file that will open that file for editing." + (let ((bib-files (bibtex-completion-normalize-bibliography 'main))) + (-concat + (--map (cons (s-concat "Create new entry in " (f-filename it)) + `(lambda (_search-expression) (find-file ,it) (goto-char (point-max)) (newline))) + bib-files) + bibtex-completion-fallback-options))) + +(defun bibtex-completion-find-local-bibliography () + "Return a list of BibTeX files associated with the current file. + +If the current file is a BibTeX file, return this file. In LaTeX +documents, use `reftex' to find associated BibTeX files. In org +files return the local or global org bibliography (see oc.el). +If all fails, return nil." + (or (and (buffer-file-name) + (string= (or (f-ext (buffer-file-name)) "") "bib") + (list (buffer-file-name))) + ;; LaTeX: + (and (buffer-file-name) + (string= (or (f-ext (buffer-file-name)) "") "tex") + (require 'reftex-cite nil t) + (ignore-errors (reftex-get-bibfile-list))) + ;; Org (with oc.el): + (and (buffer-file-name) + (string= (or (f-ext (buffer-file-name)) "") "org") + (fboundp 'org-cite-list-bibliography-files) + (org-cite-list-bibliography-files)))) + +(defun bibtex-completion-get-key-bibtex () + "Return the key of the BibTeX entry at point, nil otherwise. +This function can be used by `bibtex-completion-key-at-point' to +find the key of the BibTeX entry at point in a BibTeX-mode +buffer." + (when (eq major-mode 'bibtex-mode) + (save-excursion + (bibtex-beginning-of-entry) + (and (looking-at bibtex-entry-maybe-empty-head) + (bibtex-key-in-head))))) + +(defun bibtex-completion-get-key-latex () + "Return the key of the BibTeX entry at point, nil otherwise. +This function can be used by `bibtex-completion-key-at-point' to +find the key of the BibTeX entry at point in a LaTeX buffer." + (when (and (derived-mode-p 'latex-mode) + (require 'reftex-parse nil t)) + (save-excursion + (skip-chars-backward "[:space:],;}") + (let ((macro (reftex-what-macro 1))) + (and (stringp (car macro)) + (string-match "\\`\\\\cite\\|cite\\'" (car macro)) + ;; allow '_' in citekeys + (let ((temp-syn-table (make-syntax-table))) + (modify-syntax-entry ?_ "_" temp-syn-table) + (with-syntax-table temp-syn-table + (thing-at-point 'symbol)))))))) + +(defun bibtex-completion-get-key-org-bibtex () + "Return the key of the BibTeX entry at point, nil otherwise. +This function can be used by `bibtex-completion-key-at-point' to +find the key of the BibTeX entry at point in an Org-mode buffer." + (when (eq major-mode 'org-mode) + (let (key) + (and (setq key (org-entry-get nil + (if (boundp 'org-bibtex-key-property) + org-bibtex-key-property + "CUSTOM_ID") + t)) + ;; KEY may be the empty string the the property is + ;; present but has no value + (> (length key) 0) + key)))) + +(defun bibtex-completion-get-key-org-cite () + "Return the org-cite key at point, nil otherwise. +This function can be used by `bibtex-completion-key-at-point' to +find the org-cite key at point in an Org-mode buffer." + (when (eq major-mode 'org-mode) + (let ((el (org-element-context))) + (when (eq (car el) 'citation-reference) + (plist-get (cadr el) :key))))) + +(defvar bibtex-completion-key-at-point-functions + (list #'bibtex-completion-get-key-bibtex + #'bibtex-completion-get-key-latex + #'bibtex-completion-get-key-org-bibtex + #'bibtex-completion-get-key-org-cite) + "List of functions to use to find the BibTeX key. +The functions should take no argument and return the BibTeX +key. Stops as soon as a function returns something. +See `bibtex-completion-key-at-point' for details.") + +(defun bibtex-completion-key-at-point () + "Return the key of the BibTeX entry at point. +The functions used to match the keys are defined in +`bibtex-completion-key-at-point-functions'." + (cl-some #'identity + (mapcar #'funcall bibtex-completion-key-at-point-functions))) + +(provide 'bibtex-completion) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions obsolete) +;; coding: utf-8 +;; indent-tabs-mode: nil +;; End: + +;;; bibtex-completion.el ends here -- 2.47.3