pkg update and first config fix
org-brain not working, add org-roam
This commit is contained in:
609
lisp/org-roam-bibtex/orb-core.el
Normal file
609
lisp/org-roam-bibtex/orb-core.el
Normal file
@@ -0,0 +1,609 @@
|
||||
;;; 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:
|
||||
Reference in New Issue
Block a user