610 lines
24 KiB
EmacsLisp
610 lines
24 KiB
EmacsLisp
;;; orb-core.el --- Org Roam BibTeX: core library -*- lexical-binding: t -*-
|
||
|
||
;; Copyright © 2020-2022 Mykhailo Shevchuk
|
||
;; Copyright © 2020 Leo Vivier
|
||
|
||
;; Author: Mykhailo Shevchuk <mail@mshevchuk.com>
|
||
;; Leo Vivier <leo.vivier+dev@gmail.com>
|
||
;; URL: https://github.com/org-roam/org-roam-bibtex
|
||
|
||
;; This file is NOT part of GNU Emacs.
|
||
|
||
;; 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, 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; see the file LICENSE. If not, visit
|
||
;; <https://www.gnu.org/licenses/>.
|
||
|
||
;;; Commentary:
|
||
;;
|
||
;; This file provides org-roam-bibtex' dependencies and thus should
|
||
;; normally be required by org-roam-bibtex feature libraries. It
|
||
;; defines customize groups and provides general utility functions
|
||
;; that depend on extra features provided through org-roam,
|
||
;; bibtex-completion and their dependencies.
|
||
|
||
;;; Code:
|
||
|
||
;; ============================================================================
|
||
;;; Dependencies
|
||
;; ============================================================================
|
||
|
||
(require 'orb-utils)
|
||
(require 'orb-compat)
|
||
|
||
(eval-when-compile
|
||
(require 'cl-macs)
|
||
(require 'subr-x)
|
||
(require 'rx))
|
||
|
||
(declare-function
|
||
bibtex-completion-get-entry "bibtex-completion" (entry-key))
|
||
(declare-function
|
||
bibtex-completion-get-value "bibtex-completion" (field entry &optional default))
|
||
(declare-function
|
||
bibtex-completion-find-pdf (key-or-entry &optional find-additional))
|
||
|
||
|
||
;; ============================================================================
|
||
;;; Customize groups
|
||
;; ============================================================================
|
||
;;
|
||
;; All modules should put their `defgroup' definitions here
|
||
;; Defcustom definitions should stay in respective files
|
||
|
||
(defgroup org-roam-bibtex nil
|
||
"Org-roam integration with BibTeX software."
|
||
:group 'org-roam
|
||
:prefix "orb-")
|
||
|
||
(defgroup orb-note-actions nil
|
||
"Orb Note Actions - run actions in note's context."
|
||
:group 'org-roam-bibtex
|
||
:prefix "orb-note-actions-")
|
||
|
||
(defgroup orb-pdf-scrapper nil
|
||
"Orb PDF Scrapper - retrieve references from PDF."
|
||
:group 'org-roam-bibtex
|
||
:prefix "orb-pdf-scrapper-")
|
||
|
||
(defgroup orb-anystyle nil
|
||
"Elisp interface to `anystyle-cli`."
|
||
:group 'org-roam-bibtex
|
||
:prefix "orb-anystyle-")
|
||
|
||
(defgroup orb-autokey nil
|
||
"Automatic generation of BibTeX citation keys."
|
||
:group 'org-roam-bibtex
|
||
:prefix "orb-autokey-")
|
||
|
||
|
||
;; ============================================================================
|
||
;;; BibTeX fields and their special handling
|
||
;; ============================================================================
|
||
|
||
(defcustom orb-bibtex-field-aliases
|
||
'(("=type=" . "entry-type")
|
||
("=key=" . "citekey")
|
||
("=has-pdf=" . "pdf?")
|
||
("=has-note=" . "note?")
|
||
("citation-number" . "#"))
|
||
"Alist of ORB-specific field aliases of the form (FIELD . ALIAS).
|
||
The ALIAS can be used instead of the FIELD anywhere in ORB's
|
||
configuration. This variable is useful to replace
|
||
`bibtex-completion''s internal '='-embraced virtual fields with
|
||
more casual alternatives."
|
||
:group 'org-roam-bibtex
|
||
:type '(repeat
|
||
(cons (string :tag "Field name")
|
||
(string :tag "Alias name"))))
|
||
|
||
(defcustom orb-attached-file-extensions '("pdf")
|
||
"When retrieving an attached file, keep files with only these extensions.
|
||
This is a list of file extensions without a dot as case-insensitive
|
||
strings.
|
||
|
||
Set it to nil to keep all file names regardless of their extensions.
|
||
|
||
BibTeX entries are searched for attached files according to
|
||
`bibtex-completion-pdf-field' (default `file') and in
|
||
BibDesk-specific `Bdsk-File-N' fields."
|
||
:group 'org-roam-bibtex
|
||
:type '(repeat :tag "List of extensions" (string)))
|
||
|
||
(defcustom orb-abbreviate-file-name t
|
||
"Non-nil to force abbreviation of file names by `orb-get-attached-file'.
|
||
When this option is set to a non-nil value, the filename returned
|
||
by `orb-get-attached-file' will get the home directory part
|
||
abbreviated to `~/'. Symlinked directories will be abbreviated
|
||
according to `directory-abbrev-alist', see `abbreviate-file-name'
|
||
for details.
|
||
|
||
An as-is value will be used otherwise."
|
||
:group 'org-roam-bibtex
|
||
:type '(choice
|
||
(const :tag "Yes" t)
|
||
(const :tag "No" nil)))
|
||
|
||
(defcustom orb-open-attached-file-as-truename t
|
||
"Non-nil to open attached files with their true names.
|
||
When this option is set non-nil, `orb-open-attached-file' will
|
||
open files using their true names. You may want to set it to nil
|
||
if using file symlinks and experiencing problems such as
|
||
discussed here:
|
||
|
||
https://github.com/org-roam/org-roam-bibtex/issues/259
|
||
|
||
An as-is value will be used otherwise."
|
||
:group 'org-roam-bibtex
|
||
:type '(choice
|
||
(const :tag "Yes" t)
|
||
(const :tag "No" nil)))
|
||
|
||
|
||
(defcustom orb-use-bibdesk-attachments nil
|
||
"Whether to look up BibDesk-specific file fields `Bdsk-File'.
|
||
If this is non-nil, attachments given in BibDesk-specific file
|
||
fields will be considered in addition to those found through the
|
||
`bibtex-completion-find-pdf' mechanism when performing a template
|
||
expansion, opening an attachment with `orb-note-actions' or
|
||
scraping a PDF with `orb-pdf-scrapper'.
|
||
|
||
Duplicates will be resolved, but since duplicate comparison is
|
||
performed using `file-truename', this will lead to expansion of
|
||
symlink paths if such are used in the normal BibTeX `file' field,
|
||
for example. See also `orb-abbreviate-file-name' on how to
|
||
abbreviate the retrieved filenames.
|
||
|
||
Set this to symbol `only' to look up only BibDesk attachments and
|
||
do not use `bibtex-completion-find-pdf'."
|
||
:group 'org-roam-bibtex
|
||
:type '(choice
|
||
(const :tag "Yes" t)
|
||
(const :tag "BibDesk only" only)
|
||
(const :tag "No" nil)))
|
||
|
||
(defsubst orb-resolve-field-alias (alias)
|
||
"Return ALIAS association from `orb-bibtex-field-aliases'.
|
||
Return ALIAS if association was not found."
|
||
(or (car (rassoc alias orb-bibtex-field-aliases)) alias))
|
||
|
||
(defun orb-get-bibdesk-filenames (entry)
|
||
"Return filenames stored in BibDesk file fields \"Bdsk-File-N\".
|
||
ENTRY is a BibTeX entry as returned by `bibtex-completion-get-entry'.
|
||
|
||
The variable `orb-attached-file-extensions' is respected."
|
||
;; NOTE: Mac-specific, hard-coded
|
||
(let* ((bdsk-file-fields
|
||
(seq-filter (lambda (cell)
|
||
(string-match-p "Bdsk-File" (car cell)))
|
||
entry))
|
||
(strip-value-rx
|
||
(rx (seq (opt (in "\"{"))
|
||
(group (* (not (in "\"{}"))))
|
||
(opt (in "\"}")))))
|
||
(filename-rx
|
||
(concat
|
||
(rx (seq "Users/" (* anychar)))
|
||
(if orb-attached-file-extensions
|
||
(regexp-opt orb-attached-file-extensions t)
|
||
"pdf")))
|
||
(bdsk-files
|
||
(mapcar
|
||
(lambda (cell)
|
||
(let ((val (cdr cell))
|
||
file)
|
||
(when (string-match strip-value-rx val)
|
||
(setq file (base64-decode-string (match-string 1 val)))
|
||
(when (string-match filename-rx file)
|
||
(concat "/" (match-string 0 file))))))
|
||
bdsk-file-fields)))
|
||
(seq-filter (lambda (val) val) bdsk-files)))
|
||
|
||
;;;###autoload
|
||
(defun orb-get-attached-file (citekey)
|
||
"Look up files associated with a BibTeX entry identified by CITEKEY.
|
||
Files are searched for using `bibtex-completion-find-pdf',
|
||
meaning that Mendeley, Zotero and plain file paths are all
|
||
supported, and variables `bibtex-completion-pdf-field' and
|
||
`bibtex-completion-library-path' are respected. Additionally,
|
||
the BibTeX entry is searched for BibDesk-specific file fields
|
||
`Bdsk-File-N'.
|
||
|
||
If `orb-attached-file-extensions' is non-nil, return only file paths
|
||
matching the respective extensions.
|
||
|
||
If `orb-abbreviate-file-name' is non-nil, force an abbreviated
|
||
file name.
|
||
|
||
Depending on the value of `orb-use-bibdesk-attachments', the
|
||
BibDesk-specific file fields `Bdsk-File-N' may or may not be used
|
||
for the lookup.
|
||
|
||
If multiple files have been found, the user will be prompted to
|
||
select one."
|
||
(condition-case err
|
||
(when-let* ((entry (bibtex-completion-get-entry citekey))
|
||
(paths
|
||
(--> (pcase orb-use-bibdesk-attachments
|
||
(`nil (bibtex-completion-find-pdf
|
||
entry bibtex-completion-find-additional-pdfs))
|
||
(`only (orb-get-bibdesk-filenames entry))
|
||
(_
|
||
(-->
|
||
(nconc (bibtex-completion-find-pdf entry)
|
||
(orb-get-bibdesk-filenames entry))
|
||
(-map #'file-truename it)
|
||
(-uniq it))))
|
||
(if (not orb-attached-file-extensions)
|
||
it ; do not filter by extensions
|
||
;; filter by extensions
|
||
(--filter
|
||
(when-let ((ext (file-name-extension it)))
|
||
(member-ignore-case ext orb-attached-file-extensions))
|
||
it))))
|
||
(path (if (cdr paths)
|
||
(completing-read "File to use: " paths)
|
||
(car paths))))
|
||
(if orb-abbreviate-file-name
|
||
(abbreviate-file-name path)
|
||
path))
|
||
;; ignore any errors that may be thrown by `bibtex-completion-find-pdf'
|
||
;; don't stop the capture process
|
||
(error
|
||
(orb-warning
|
||
(format "error in `orb-get-attached-file`: %s %s"
|
||
(car err) (cdr err))))))
|
||
|
||
;;;###autoload
|
||
(defun orb-open-attached-file (citekey)
|
||
"Open a file associated with CITEKEY.
|
||
CITEKEY must be a list for compatibility with `bibtex-completion'
|
||
functions, which also expect a list.
|
||
|
||
This is a modified and simplified version of `bibtex-completion-open-pdf',
|
||
which uses `orb-get-bibdesk-filenames' under the hood and is therefore
|
||
compatible with BibDesk. The file is opened with the function set in
|
||
`bibtex-completion-pdf-open-function'.
|
||
|
||
The intended primary use is with `orb-note-actions'."
|
||
(let* ((key (car citekey))
|
||
(attachment (orb-get-attached-file key)))
|
||
(if attachment
|
||
(funcall bibtex-completion-pdf-open-function
|
||
(if orb-open-attached-file-as-truename
|
||
(file-truename attachment)
|
||
attachment))
|
||
(message "No PDF(s) found for this entry: %s" key))))
|
||
|
||
;; ============================================================================
|
||
;;;; Orb autokey
|
||
;; ============================================================================
|
||
|
||
(defcustom orb-autokey-format "%a%y%T[4][1]"
|
||
"Format string for automatically generated citation keys.
|
||
|
||
Supported wildcards:
|
||
|
||
Basic
|
||
==========
|
||
|
||
%a |author| - first author's (or editor's) last name
|
||
%t |title | - first word of title
|
||
%f{field} |field | - first word of arbitrary field
|
||
%y |year | - year YYYY
|
||
%p |page | - first page
|
||
%e{(expr)} |elisp | - execute elisp expression
|
||
|
||
Extended
|
||
==========
|
||
|
||
1. Capitalized versions:
|
||
|
||
%A |author| >
|
||
%T |title | > Same as %a,%t,%f{field} but
|
||
%F{field} |field | > preserve original capitalization
|
||
|
||
2. Starred versions
|
||
|
||
%a*, %A* |author| - include author's (editor's) initials
|
||
%t*, %T* |title | - do not ignore words in `orb-autokey-titlewords-ignore'
|
||
%y* |year | - year's last two digits __YY
|
||
%p* |page | - use \"pagetotal\" field instead of default \"pages\"
|
||
|
||
3. Optional parameters
|
||
|
||
%a[N][M][D] |author| >
|
||
%t[N][M][D] |title | > include first N words/names
|
||
%f{field}[N][M][D] |field | > include at most M first characters of word/name
|
||
%p[D] |page | > put delimiter D between words
|
||
|
||
N and M should be a single digit 1-9. Putting more digits or any
|
||
other symbols will lead to ignoring the optional parameter and
|
||
those following it altogether. D should be a single alphanumeric
|
||
symbol or one of `-_.:|'.
|
||
|
||
Optional parameters work both with capitalized and starred
|
||
versions where applicable.
|
||
|
||
4. Elisp expression
|
||
|
||
- can be anything
|
||
- should return a string or nil
|
||
- will be evaluated before expanding other wildcards and therefore
|
||
can insert other wildcards
|
||
- will have `entry' variable bound to the value of BibTeX entry the key
|
||
is being generated for, as returned by `bibtex-completion-get-entry'.
|
||
The variable may be safely manipulated in a destructive manner.
|
||
|
||
%e{(or (bibtex-completion-get-value \"volume\" entry) \"N/A\")}
|
||
%e{(my-function entry)}
|
||
|
||
Key generation is performed by `orb-autokey-generate-key'."
|
||
:risky t
|
||
:type 'string
|
||
:group 'org-roam-bibtex)
|
||
|
||
(defcustom orb-autokey-titlewords-ignore
|
||
'("A" "An" "On" "The" "Eine?" "Der" "Die" "Das"
|
||
"[^[:upper:]].*" ".*[^[:upper:][:lower:]0-9].*")
|
||
"Patterns from title that will be ignored during key generation.
|
||
Every element is a regular expression to match parts of the title
|
||
that should be ignored during automatic key generation. Case
|
||
sensitive."
|
||
;; Default value was take from `bibtex-autokey-titleword-ignore'.
|
||
:type '(repeat :tag "Regular expression" regexp)
|
||
:group 'orb-autokey)
|
||
|
||
(defcustom orb-autokey-empty-field-token "N/A"
|
||
"String to use when BibTeX field is nil or empty."
|
||
:type 'string
|
||
:group 'orb-autokey)
|
||
|
||
(defcustom orb-autokey-invalid-symbols
|
||
" \"'()={},~#%\\"
|
||
"Characters not allowed in a BibTeX key.
|
||
The key will be stripped of these characters."
|
||
:type 'string
|
||
:group 'orb-autokey)
|
||
|
||
(defun orb--autokey-format-field (field &rest specs)
|
||
"Return BibTeX FIELD formatted according to plist SPECS.
|
||
|
||
Recognized keys:
|
||
==========
|
||
:entry - BibTeX entry to use
|
||
:value - Value of BibTeX field to use
|
||
instead retrieving it from :entry
|
||
:capital - capitalized version
|
||
:starred - starred version
|
||
:words - first optional parameter (number of words)
|
||
:characters - second optional parameter (number of characters)
|
||
:delimiter - third optional parameter (delimiter)
|
||
|
||
All values should be strings, including those representing numbers.
|
||
|
||
This function is used internally by `orb-autokey-generate-key'."
|
||
(declare (indent 1))
|
||
(-let* (((&plist :entry entry
|
||
:value value
|
||
:capital capital
|
||
:starred starred
|
||
:words words
|
||
:characters chars
|
||
:delimiter delim) specs)
|
||
;; field values will be split into a list of words. `separator' is a
|
||
;; regexp for word separators: either a whitespace, one or more
|
||
;; dashes, or en dash, or em dash
|
||
(separator "\\([ \n\t]\\|[-]+\\|[—–]\\)")
|
||
(invalid-chars-rx
|
||
(rx-to-string `(any ,orb-autokey-invalid-symbols) t))
|
||
(delim (or delim ""))
|
||
result)
|
||
;; 0. virtual field "=name=" is used internally here and in
|
||
;; `orb-autokey-generate-key'; it stands for author or editor
|
||
(if (string= field "=name=")
|
||
;; in name fields, logical words are full names consisting of several
|
||
;; words and containing spaces and punctuation, separated by a logical
|
||
;; separator, the word "and"
|
||
(setq separator " and "
|
||
value (or value
|
||
(bibtex-completion-get-value "author" entry)
|
||
(bibtex-completion-get-value "editor" entry)))
|
||
;; otherwise proceed with value or get it from entry
|
||
(setq value (or value
|
||
(bibtex-completion-get-value field entry))))
|
||
(if (or (not value)
|
||
(string-empty-p value))
|
||
(setq result orb-autokey-empty-field-token)
|
||
(when (> (length value) 0)
|
||
(save-match-data
|
||
;; 1. split field into words
|
||
(setq result (split-string value separator t "[ ,.;:-]+"))
|
||
;; 1a) only for title;
|
||
;; STARRED = include words from `orb-autokey-titlewords-ignore
|
||
;; unstarred version filters the keywords, starred ignores this block
|
||
(when (and (string= field "title")
|
||
(not starred))
|
||
(let ((ignore-rx (concat "\\`\\(:?"
|
||
(mapconcat #'identity
|
||
orb-autokey-titlewords-ignore
|
||
"\\|") "\\)\\'"))
|
||
(words ()))
|
||
(setq result (dolist (word result (nreverse words))
|
||
(unless (string-match-p ignore-rx word)
|
||
(push word words))))))
|
||
;; 2. take number of words equal to WORDS if that is set
|
||
;; or just the first word; also 0 = 1.
|
||
(if words
|
||
(setq words (string-to-number words)
|
||
result (-take (if (> words (length result))
|
||
(length result)
|
||
words)
|
||
result))
|
||
(setq result (list (car result))))
|
||
;; 2a) only for "=name=" field, i.e. author or editor
|
||
;; STARRED = include initials
|
||
(when (string= field "=name=")
|
||
;; NOTE: here we expect name field 'Doe, J. B.'
|
||
;; should ideally be able to handle 'Doe, John M. Longname, Jr'
|
||
(let ((r-x (if starred
|
||
"[ ,.\t\n]"
|
||
"\\`\\(.*?\\),.*\\'"))
|
||
(rep (if starred "" "\\1"))
|
||
(words ()))
|
||
(setq result
|
||
(dolist (name result (nreverse words))
|
||
(push (s-replace-regexp r-x rep name) words)))))
|
||
;; 3. take at most CHARS number of characters from every word
|
||
(when chars
|
||
(let ((words ()))
|
||
(setq chars (string-to-number chars)
|
||
result (dolist (word result (nreverse words))
|
||
(push
|
||
(substring word 0
|
||
(if (< chars (length word))
|
||
chars
|
||
(length word)))
|
||
words)))))
|
||
;; 4. almost there: concatenate words, include DELIMiter
|
||
(setq result (mapconcat #'identity result delim))
|
||
;; 5. CAPITAL = preserve case
|
||
(unless capital
|
||
(setq result (downcase result))))))
|
||
;; return result stripped of the invalid characters
|
||
(s-replace-regexp invalid-chars-rx "" result t)))
|
||
|
||
(defun orb--autokey-evaluate-expression (expr &optional entry)
|
||
"Evaluate arbitrary elisp EXPR passed as readable string.
|
||
The expression will have value of ENTRY bound to `entry' variable
|
||
at its disposal. ENTRY should be a BibTeX entry as returned by
|
||
`bibtex-completion-get-entry'. The result returned should be a
|
||
string or nil."
|
||
(let ((result (eval `(let ((entry (quote ,(copy-tree entry))))
|
||
,(read expr)))))
|
||
(unless (or (stringp result)
|
||
(not result))
|
||
(user-error "Result: %s, invalid type. \
|
||
Expression must be string or nil" result))
|
||
(or result "")))
|
||
|
||
;;;###autoload
|
||
(defun orb-autokey-generate-key (entry &optional control-string)
|
||
"Generate citation key from ENTRY according to `orb-autokey-format'.
|
||
Return a string. If optional CONTROL-STRING is non-nil, use it
|
||
instead of `orb-autokey-format'."
|
||
(let* ((case-fold-search nil)
|
||
(str (or control-string orb-autokey-format))
|
||
;; star regexp: group 3!
|
||
(star '(opt (group-n 3 "*")))
|
||
;; optional parameters: regexp groups 4-6!
|
||
(opt1 '(opt (and "[" (opt (group-n 4 digit)) "]")))
|
||
(opt2 '(opt (and "[" (opt (group-n 5 digit)) "]")))
|
||
(opt3 '(opt (and "[" (opt (group-n 6 (any alnum "_.:|-"))) "]")))
|
||
;; capital letters: regexp group 2!
|
||
;; author wildcard regexp
|
||
(a-rx (macroexpand
|
||
`(rx (group-n 1 (or "%a" (group-n 2 "%A"))
|
||
,star ,opt1 ,opt2 ,opt3))))
|
||
;; title wildcard regexp
|
||
(t-rx (macroexpand
|
||
`(rx (group-n 1 (or "%t" (group-n 2 "%T"))
|
||
,star ,opt1 ,opt2 ,opt3))))
|
||
;; any field wildcard regexp
|
||
;; required parameter: group 7!
|
||
(f-rx (macroexpand
|
||
`(rx (group-n 1 (or "%f" (group-n 2 "%F"))
|
||
(and "{" (group-n 7 (1+ letter)) "}")
|
||
,opt1 ,opt2 ,opt3))))
|
||
;; year wildcard regexp
|
||
(y-rx (rx (group-n 1 "%y" (opt (group-n 3 "*")))))
|
||
;; page wildcard regexp
|
||
(p-rx (macroexpand `(rx (group-n 1 "%p" ,star ,opt3))))
|
||
;; elisp expression wildcard regexp
|
||
;; elisp sexp: group 8!
|
||
(e-rx (rx (group-n 1 "%e"
|
||
"{" (group-n 8 "(" (1+ ascii) ")") "}"))))
|
||
;; Evaluating elisp expression should go the first because it can produce
|
||
;; additional wildcards
|
||
(while (string-match e-rx str)
|
||
(setq str (replace-match
|
||
(save-match-data
|
||
(orb--autokey-evaluate-expression
|
||
(match-string 8 str) entry)) t nil str 1)))
|
||
;; Expanding all other wildcards are actually
|
||
;; variations of calls to `orb--autokey-format-field' with many
|
||
;; commonalities, so we wrap it into a macro
|
||
(cl-macrolet
|
||
((expand
|
||
(wildcard &key field value entry capital
|
||
starred words characters delimiter)
|
||
(let ((cap (or capital '(match-string 2 str)))
|
||
(star (or starred '(match-string 3 str)))
|
||
(opt1 (or words '(match-string 4 str)))
|
||
(opt2 (or characters '(match-string 5 str)))
|
||
(opt3 (or delimiter '(match-string 6 str))))
|
||
`(while (string-match ,wildcard str)
|
||
(setq str (replace-match
|
||
;; we can safely pass nil values
|
||
;; `orb--autokey-format-field' should
|
||
;; handle them correctly
|
||
(orb--autokey-format-field ,field
|
||
:entry ,entry :value ,value
|
||
:capital ,cap :starred ,star
|
||
:words ,opt1 :characters ,opt2 :delimiter ,opt3)
|
||
t nil str 1))))))
|
||
;; Handle author wildcards
|
||
(expand a-rx
|
||
:field "=name="
|
||
:value (or (bibtex-completion-get-value "author" entry)
|
||
(bibtex-completion-get-value "editor" entry)))
|
||
;; Handle title wildcards
|
||
(expand t-rx
|
||
:field "title"
|
||
:value (or (bibtex-completion-get-value "title" entry) ""))
|
||
;; Handle custom field wildcards
|
||
(expand f-rx
|
||
:field (match-string 7 str)
|
||
:entry entry)
|
||
;; Handle pages wildcards %p*[-]
|
||
(expand p-rx
|
||
:field (if (match-string 3 str)
|
||
"pagetotal" "pages")
|
||
:entry entry
|
||
:words "1"))
|
||
;; Handle year wildcards
|
||
;; it's simple, so we do not use `orb--autokey-format-field' here
|
||
;; year should be well-formed: YYYY
|
||
;; TODO: put year into cl-macrolet
|
||
(let ((year (or (bibtex-completion-get-value "year" entry)
|
||
(bibtex-completion-get-value "date" entry))))
|
||
(if (or (not year)
|
||
(string-empty-p year)
|
||
(string= year orb-autokey-empty-field-token))
|
||
(while (string-match y-rx str)
|
||
(setq str (replace-match orb-autokey-empty-field-token
|
||
t nil str 1)))
|
||
(while (string-match y-rx str)
|
||
(setq year (format "%04d" (string-to-number year))
|
||
str (replace-match
|
||
(format "%s" (if (match-string 3 str)
|
||
(substring year 2 4)
|
||
(substring year 0 4)))
|
||
t nil str 1)))))
|
||
str))
|
||
|
||
(provide 'orb-core)
|
||
;;; orb-core.el ends here
|
||
;; Local Variables:
|
||
;; coding: utf-8
|
||
;; fill-column: 79
|
||
;; End:
|