-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathebib-utils.el
2827 lines (2510 loc) · 129 KB
/
ebib-utils.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; ebib-utils.el --- Part of Ebib, a BibTeX database manager -*- lexical-binding: t -*-
;; Copyright (c) 2003-2024 Joost Kremers
;; Copyright (c) 2022-2024 Hugo Heagren
;; All rights reserved.
;; Author: Joost Kremers <[email protected]>
;; Maintainer: Joost Kremers <[email protected]>
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;;
;; 1. Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; 2. Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;; 3. The name of the author may not be used to endorse or promote products
;; derived from this software without specific prior written permission.
;;
;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE,
;; DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;;; Commentary:
;; This file is part of Ebib, a BibTeX database manager for Emacs. It contains
;; general macros, utilities and variables.
;;; Code:
(require 'cl-lib)
(require 'seq)
(require 'bibtex)
(require 'format-spec)
(require 'message) ; for `message-unquote-tokens'.
(require 'parsebib)
(require 'ebib-db)
;; Make a bunch of variables obsolete.
(make-obsolete-variable 'ebib-entry-types "The variabale `ebib-entry-types' is obsolete; see the manual for details." "Ebib 2.5.4")
(make-obsolete-variable 'ebib-default-entry 'ebib-default-entry-type "Ebib 2.5.4")
(make-obsolete-variable 'ebib-additional-fields 'ebib-extra-fields "Ebib 2.5.4")
(make-obsolete-variable 'ebib-biblatex-inheritance 'ebib-biblatex-inheritances "Ebib 2.5.4")
;; Make sure we can call bibtex-generate-autokey.
(declare-function bibtex-generate-autokey "bibtex" nil)
;;;;;;;;;;;;;;;;;;;;;;
;; global variables ;;
;;;;;;;;;;;;;;;;;;;;;;
;; User customisation.
(defgroup ebib nil "Ebib: a BibTeX database manager." :group 'tex)
(defgroup ebib-windows nil "Ebib window management." :group 'ebib)
(defcustom ebib-default-entry-type "Article"
"The default entry type.
This is the entry type assigned to newly created entries."
:group 'ebib
:type 'string)
;;;###autoload (put 'ebib-preload-bib-files 'safe-local-variable '(lambda (val) (seq-every-p #'stringp val)))
(defcustom ebib-preload-bib-files nil
"List of BibTeX files to load automatically when Ebib starts.
This option allows you to specify which `.bib' file(s) Ebib
should load automatically when it starts up. Specify one file per
line. You can complete a partial filename with `M-TAB`."
:group 'ebib
:type '(repeat (file :must-match t)))
(defcustom ebib-local-bibfiles t
"List of a buffer's .bib file(s).
A value of t means that this variable has not been initialized
yet in the relevant buffer. A value of nil means that there are
no local .bib files. This variable can be used as a file-local
variable."
:group 'ebib
:type '(choice (const :tag "Use `ebib-preload-bib-files'" t)
(repeat :tag "Specify .bib files" (file :must-match t))))
(put 'ebib-local-bibfiles 'safe-local-variable (lambda (v) (null (seq-remove #'stringp v))))
(defcustom ebib-bib-search-dirs '("~")
"List of directories to search for BibTeX files.
This is a list of directories Ebib searches for `.bib' files to
be preloaded. Note that only the directories themselves are
searched, not their subdirectories. The directories in this list
are also searched when the function `ebib' is passed a file
name (e.g., from an Eshell command line)."
:group 'ebib
:type '(repeat :tag "Search directories for BibTeX files" (string :tag "Directory")))
(defcustom ebib-default-directory nil
"The default directory for Ebib.
This mainly determines which directory is the default directory
when reading a file name from the user. Possible values are nil,
which means to use the directory Ebib is started in, the symbol
`first-bib-dir', which means to use the first directory listed in
`ebib-bib-serch-dirs', or a directory path.
In order for this option to take effect, you need to restart Ebib."
:group 'ebib
:type '(choice (const :tag "Use Ebib startup directory" nil)
(const :tag "Use first .bib directory" first-bib-dir)
(directory :tag "Use a specific directory")))
(defcustom ebib-create-backups t
"Create a backup file.
The first time a BibTeX file is saved, a backup file is created
when it is first saved. Note that Ebib uses
`make-backup-file-name' to create the name for the backup file."
:group 'ebib
:type '(choice (const :tag "Create backups" t)
(const :tag "Do not create backups" nil)))
(defcustom ebib-extra-fields '((biblatex "crossref"
"xdata"
"annotation"
"abstract"
"keywords"
"file"
"timestamp")
(BibTeX "crossref"
"annote"
"abstract"
"keywords"
"file"
"timestamp"
"url"
"doi"))
"List of the extra fields for BibTeX entries.
Extra fields are fields that are available for all entry types.
Depending on the bibliography style, the value of these fields
may appear in the bibliography, but you may also define fields
that are just for personal use.
Note, before adding fields to this list, check if the field you
want to add is among the fields that are hidden by default. See
the option \"Hidden Fields\" (`ebib--hidden-fields') for details."
:group 'ebib
:type '(set (cons :tag "Dialect" (const biblatex) (repeat :tag "Extra fields" (string :tag "Field")))
(cons :tag "Dialect" (const BibTeX) (repeat :tag "Extra fields" (string :tag "Field")))))
(defcustom ebib-hidden-fields '("addendum"
"afterword"
"annotator"
"archiveprefix"
"bookauthor"
"booksubtitle"
"booktitleaddon"
"chapter"
"commentator"
"edition"
"editora"
"editorb"
"editorc"
"eid"
"eprint"
"eprintclass"
"eprinttype"
"eventdate"
"eventtitle"
"foreword"
"holder"
"howpublished"
"introduction"
"isbn"
"isrn"
"issn"
"issue"
"issuesubtitle"
"issuetitle"
"issuetitleaddon"
"journaltitleadddon"
"journalsubtitle"
"language"
"location"
"mainsubtitle"
"maintitle"
"maintitleaddon"
"month"
"origlanguage"
"pagetotal"
"part"
"primaryclass"
"remark"
"subtitle"
"timestamp"
"titleaddon"
"translator"
"urldate"
"venue"
"version"
"volumes")
"List of fields that are not displayed when empty.
The value is a list of field names (as case-insensitive strings).
The value can also be t, in which case all fields are treated as
hidden, i.e., they are only displayed when they have a value.q"
:group 'ebib
:type '(choice (const :tag "All fields" t)
(repeat :tag "Specify fields" (string :tag "Field"))))
(defcustom ebib-always-prompt-for-special-keys t
"Prompt for new key in already-named Xdata, Set entries."
:group 'ebib
:type 'boolean)
(defcustom ebib-layout 'full
"Ebib window layout.
This option defines how Ebib displays the buffers it uses. Possible values are:
- `full' (default): Use the entire frame. The existing windows
are hidden and reappear when Ebib is lowered.
- `window': Use the current window. This window is split up to
display the index and entry buffers.
- `custom': Use part of the current frame. The width of the Ebib
windows can be set with the option `ebib-width'.
- `index-only': Display only the index window. The entry buffer
is displayed when the user edits an entry or after pressing
\\<ebib-index-mode-map>\\[ebib-select-and-popup-entry]."
:group 'ebib-windows
:type '(choice (const :tag "Use full frame" full)
(const :tag "Use current window" window)
(const :tag "Use right part of the frame" custom)
(const :tag "Display only index window" index-only)))
(defcustom ebib-width 80
"Width of the Ebib windows.
The width can be absolute or relative; if it is absolute, it
specifies the number of columns that the Ebib windows occupies.
If it is relative, the with must be a value between 0 and 1
specifying the width relative to the width of the window that is
selected when Ebib is started.
This option only takes effect if `ebib-layout' is set to `custom'."
:group 'ebib-windows
:type '(choice (integer :tag "Absolute width")
(float :tag "Relative width" :value 0.3)))
(defcustom ebib-popup-entry-window nil
"Create a popup window to display the entry window.
If `ebib-layout' is set to `index-only', Ebib will use an
existing window to display the entry buffer when needed. By
setting this option, however, you can tell Ebib to use the
function `display-buffer-pop-up-window' to show the entry buffer,
which (usually) means that a new window will be created.
Note that setting this option has no effect unless `ebib-layout'
is set to `index-only'."
:group 'ebib-windows
:type 'boolean)
(defcustom ebib-window-vertical-split nil
"Display the index buffer at the left of the frame.
Setting this option makes Ebib display the index buffer at the
left side of the frame rather than at the top. The width of the
window will be `ebib-index-window-size', which you will probably
have to set to a larger value."
:group 'ebib-windows
:type 'boolean)
(defcustom ebib-index-window-size 10
"The size of the index buffer window.
This is either the height of the window, or, if
`ebib-window-vertical-split' is set, the width of the window.
The rest of the frame is used for the entry buffer, unless
`ebib-layout' is set to `index-only'."
:group 'ebib-windows
:type 'integer)
(defcustom ebib-index-mode-line '("%e"
mode-line-front-space
ebib--mode-line-modified
mode-line-buffer-identification
(:eval (if (and ebib--cur-db (ebib-db-dependent-p ebib--cur-db))
(format " [%s]" (ebib-db-get-filename (ebib-db-get-main ebib--cur-db) t))))
(:eval (format " (%s)" (ebib--get-dialect ebib--cur-db)))
(:eval (if (and ebib--cur-db (ebib--get-key-at-point))
" Entry %l"
" No Entries"))
(:eval (if (and ebib--cur-db (ebib-db-get-filter ebib--cur-db))
(format " |%s|" (ebib--filters-pp-filter (ebib-db-get-filter ebib--cur-db)))
"")))
"The mode line for the index window.
The mode line of the index window shows some Ebib-specific
information. You can customize this information if you wish, or
disable the Ebib-specific mode line altogether."
:group 'ebib-windows
:type '(choice (const :tag "Use standard mode line" nil)
(sexp :tag "Customize mode line")))
(defcustom ebib-entry-mode-line '((:eval (ebib--format-entry-info-for-modeline)))
"The mode line for the entry buffer.
The mode line of the entry window shows the entry key. You can
customize this information if you wish, or disable the
Ebib-specific mode line altogether."
:group 'ebib-windows
:type '(choice (const :tag "Disable mode line" nil)
(sexp :tag "Customize mode line")))
(defvar ebib--mode-line-modified '(:eval (ebib--mode-line-modified-p))
"Mode line construct for database's modified status.")
(put 'ebib--mode-line-modified 'risky-local-variable t)
(defcustom ebib-modified-char "M"
"Character indicating the modified status in the mode line."
:group 'ebib-windows
:type 'string)
(defcustom ebib-index-columns '(("Entry Key" 40 t)
("Author/Editor" 40 t)
("Year" 6 t)
("Title" 50 t))
"Columns to display in the index buffer.
Each column consists of the BibTeX field to be displayed, which
is also the column's label, the column's maximum width and a flag
indicating whether sorting on this column is possible.
Any BibTeX or biblatex field can be used as a label. There are
also two special labels: \"Entry Key\" and \"Author/Editor\".
The label \"Entry Key\" displays the entry's BibTeX key, and the
label \"Author/Editor\" displays the contents of the Author
field, or, if that is empty, the contents of the Editor field.
If the sorting flag is t, a sorting function is looked up in
`ebib-field-sort-functions-alist', defaulting to
`string-collate-lessp' if none is found. If it is nil, sorting is
impossible.
Note that the default sort field is the first field in this
option. See also `ebib-index-default-sort'."
:group 'ebib
:type '(repeat (list (string :tag "Field")
(integer :tag "Width")
(boolean :tag "Sort"))))
(defcustom ebib-index-default-sort nil
"Default sort field and direction.
If set, the index buffer is sorted on the specified field and in
the specified direction. Note that it is not necessary that the
default sort field is visible in the index buffer."
:group 'ebib
:type '(choice (const :tag "Use First Index Column" nil)
(cons :tag "User-Defined Sort"
(string :tag "Sort Field")
(choice (const :tag "Ascending Sort" ascend)
(const :tag "Descending Sort" descend)))))
(defun ebib-compare-numerical-strings (a b)
"Return t if A represents a number less than B represents.
A and B are strings (e.g. \"3\" and \"11\")."
(< (string-to-number a) (string-to-number b)))
(defcustom ebib-field-sort-functions-alist '(("Chapter" . ebib-compare-numerical-strings)
("Sortyear" . ebib-compare-numerical-strings)
("Volume" . ebib-compare-numerical-strings)
("Volumes" . ebib-compare-numerical-strings))
"Alist of bib(la)tex fields and functions for sorting the index.
Field names are case-insensitive. Sort functions should take two
arguments and should return t if the first is to be sorted before
the second."
:group 'ebib
:type '(alist :key-type string :value-type symbol))
(defcustom ebib-index-column-separator " "
"Separator between columns in the index buffer."
:group 'ebib
:type 'string)
(defcustom ebib-field-transformation-functions '(("Title" . ebib-clean-TeX-markup-from-entry)
("Doi" . ebib-display-www-link)
("Url" . ebib-display-www-link)
("Note" . ebib-notes-display-note-symbol))
"Functions transforming field contents to appropriate forms.
Each function should accept three arguments, the field to be
displayed, the key of the entry being displayed, and the database
that contains the entry, and should return a string to be
displayed in the ebib index buffer. In principle, this string
should be the contents of the field transformed in some way, but
it may actually be anything. In fact, it is not necessary for
the field to be an actual Bib(La)TeX field, as long as the
transformation function returns something that can be displayed."
:group 'ebib
:type '(repeat (cons (string :tag "Field")
(function :tag "Transform function"))))
(make-obsolete-variable 'ebib-TeX-markup-replace-alist 'parsebib-TeX-markup-replace-alist "Ebib 2.37")
(defcustom ebib-uniquify-keys nil
"Create unique keys.
When adding new entries to the database, Ebib does not allow
duplicate keys. By setting this option, you can tell Ebib to
automatically create a unique key by adding `b', `c'... to it.
This applies when Ebib automatically generates keys for new
entries (see `ebib-autogenerate-keys'), when merging `.bib'
files, and when changing a key."
:group 'ebib
:type 'boolean)
(defcustom ebib-autogenerate-keys t
"Automatically generate keys for new entries.
With this option set, Ebib does not ask for a key when you add a
new entry. Instead, it gives the entry a temporary key and
assigns a proper key when you finish editing the entry. This
option uses the function `bibtex-generate-autokey', which has a
number of user-customizable options. See that function's
documentation for details."
:group 'ebib
:type 'boolean)
(defcustom ebib-reference-templates '(("Article" . "{Author} ({Date|Year}). {\"Title\".} {Journaltitle|Journal}, {Volume}{:Issue}, {Pages}. {Doi|Url.}")
("Book" . "{Author|Editor} ({Date|Year}). {\"Title\".} {Publisher.} {Doi|Url.}")
("MvBook" . "{Author|Editor} ({Date|Year}). {\"Title\".} {Publisher.} {Doi|Url.}")
("InBook" . "{Author} ({Date|Year}). {\"Title\".} In: {Editor}, {\"Booktitle\"}. {Publisher.} {Pages.} {Doi|Url.}")
("Collection" . "{Editor} ({Date|Year}). {\"Title\".} {Publisher.} {Doi|Url.}")
("MvCollection" . "{Editor} ({Date|Year}). {\"Title\".} {Publisher.} {Doi|Url.}")
("InCollection" . "{Author} ({Date|Year}). {\"Title\".} In: {Editor}, {\"Booktitle\"}. {Publisher.} {Pages.} {Doi|Url.}")
("Misc" . "{Author|Editor} ({Date|Year}). {\"Title\".} {Howpublished.}")
("Online" . "{Author|Editor} ({Date|Year}). {\"Title\".} {Doi|Url.}")
("Proceedings" . "{Editor} ({Date|Year}). {\"Title\".} {Organization.}")
("MvProceedings" . "{Editor} ({Date|Year}). {\"Title\".} {Organization.}")
("InProceedings" . "{Author} ({Date|Year}). {\"Title\".} In: {Editor}, {\"Booktitle\"}. {Publisher.} {Pages.} {Doi|Url.}")
("Thesis" . "{Author} ({Date|Year}). {\"Title\".} {Type,} {Institution.} ")
("Unpublished" . "{Author} ({Date|Year}). {\"Title\".} {Howpublished.} {Note.}"))
"Templates for copying references to the kill ring.
Each template is a string containing literal text and directives
in braces {}. A directive should contain a field name and is
replaced with the contents of that field. A directive may
contain multiple fields separated by a pipe bar | (meaning
\"or\"), in which case the contents of the first non-empty field
replaces the directive. If all fields are empty, the directive
is simply discarded.
A directive may additionally contain punctuation before or after
the field name (or multiple field names). If the directive is
discarded because its field is empty, any punctuation inside the
braces is discarded as well."
:group 'ebib
:type '(repeat (cons (string :tag "Entry type")
(string :tag "Template string"))))
(defcustom ebib-citation-template "{Author|Editor} ({Date|Year})"
"Template used for copying a citation to the kill ring.
For details of the template format, see the user option
`ebib-reference-templates'."
:group 'ebib
:type '(string :tag "Template"))
(defvar org-link-file-path-type)
(defcustom ebib-link-file-path-type (if (boundp 'org-link-file-path-type) org-link-file-path-type 'adaptive)
"How the path name in file links should be stored.
Valid values are the same as `org-link-file-path-type'."
:group 'ebib
:type '(choice (const relative)
(const absolute)
(const noabbrev)
(const adaptive))
:safe #'symbolp)
(defun ebib--process-reference-template (template key db)
"Process TEMPLATE for KEY in DB.
TEMPLATE is a reference template (see the option
`ebib-reference-templates'). Fields in the template are replaced
with the contents of the corresponding fields in the entry KEY in
database DB."
;; Define some transformations; Perhaps these should be user-customizable.
(let ((transformations '(("Title" . parsebib-clean-TeX-markup)
("Date" . ebib-biblatex-date-to-year))))
(cl-flet ((create-replacements (match)
(save-match-data
(string-match "{\\([^A-Za-z]*\\)\\([A-Za-z|]+\\)\\([^A-Za-z]*\\)}" match)
(let* ((pre (match-string 1 match))
(fields (match-string 2 match))
(post (match-string 3 match))
(field (seq-find (lambda (field)
(ebib-get-field-value field key db 'noerror nil 'xref))
(split-string fields "|" t)))
(value (if field (ebib-get-field-value field key db 'noerror 'unbraced 'xref))))
(if value (concat pre
(funcall (alist-get field transformations #'identity nil #'cl-equalp) value)
post)
"")))))
(replace-regexp-in-string "{.*?}" #'create-replacements template nil t))))
(defcustom ebib-citation-commands '((latex-mode
(("cite" "\\cite%<[%A]%>[%A]{%(%K%,)}")
("paren" "\\parencite%<[%A]%>[%A]{%(%K%,)}")
("foot" "\\footcite%<[%A]%>[%A]{%(%K%,)}")
("text" "\\textcite%<[%A]%>[%A]{%(%K%,)}")
("smart" "\\smartcite%<[%A]%>[%A]{%(%K%,)}")
("super" "\\supercite{%K}")
("auto" "\\autocite%<[%A]%>[%A]{%(%K%,)}")
("cites" "\\cites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("parens" "\\parencites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("foots" "\\footcites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("texts" "\\textcites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("smarts" "\\smartcites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("supers" "\\supercites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("autos" "\\autoscites%<(%A)%>(%A)%(%<[%A]%>[%A]{%K}%)")
("author" "\\citeauthor%<[%A]%>[%A]{%(%K%,)}")
("title" "\\citetitle%<[%A]%>[%A]{%(%K%,)}")
("year" "\\citeyear%<[%A]%>[%A][%A]{%K}")
("date" "\\citedate%<[%A]%>[%A]{%(%K%,)}")
("full" "\\fullcite%<[%A]%>[%A]{%(%K%,)}")))
(org-mode
(("ebib" "[[ebib:%K][%D]]")))
(markdown-mode
(("text" "@%K%< [%A]%>")
("paren" "[%(%<%A %>@%K%<, %A%>%; )]")
("year" "[-@%K%< %A%>]"))))
"A list of format strings used to insert citations into text buffers.
Each item in this option consists of a major mode and a list of
identifier + format strings pairs. The identifiers (which can be
any string) are used to select the citation command in
`ebib-insert-citation' and `ebib-push-citation'. The format
strings are used to construct the citation command that is
inserted in the buffer.
The major mode can also be specified as `any', which defines
citation commands that are available in buffers that do not have
any of the major modes listed in this option.
The format string template can contain a number of formatting
directives:
%K: the key of the entry.
%A: an argument; prompts the user.
%D: a description; prompts the user.
%<...%>: optional material surrounding %A.
%(...%): a repeater, which must contain %K.
%A is used for arguments to the citation command, which are
elements such as page numbers, etc. %A accommodates optional
arguments in LaTeX-based citations and, similarly, optional
material in Pandoc Markdown citations. %D can be used to provide
a description as used in Org-mode links. The user is prompted
for this description, but if possible a default is provided,
which can be accepted by hitting RET.
Optional material around %A is only included if the user provides
some string for %A. If not, the optional material is omitted.
The command `ebib-push-citation' can be used on multiple
entries (by marking them in the index buffer). If the template
contains a repeater, the material inside this repeater is
processed for each key individually. If there is no repeater,
all keys are substituted for %K using a separator for which the
user is prompted.
The repeater can optionally contain a separator, which must be
placed between % and ); to use comma as a separator, the format
shring should contain \"%(%K%,)\". If the separator is not
provided, the user is prompted to supply one."
:group 'ebib
:type '(repeat (list :tag "Mode" (symbol :tag "Mode name")
(repeat (list :tag "Citation command"
(string :tag "Identifier")
(string :tag "Format string"))))))
(defcustom ebib-citation-insert-multiple nil
"Allow insertion of multiple citations.
If set, use `ebib-read-entry-multiple' in `ebib-insert-citation'.
This allows inserting multiple keys in one citation command but
has the disadvantage that it is not possible to provide a default
description for citations in Org buffers."
:group 'ebib
:type 'boolean)
(defcustom ebib-citation-description-function 'ebib-create-org-author/year
"Function for creating a description to be used in citations.
This function is called to provide a description to substitute
for the %D directive in `ebib-citation-commands', and also when
creating Org links with `org-store-link', provided the library
`org-ebib' is loaded.
Three predefined functions are provided. In addition, it is
possible to specify a user-defined function, which should take
two arguments: the key of the entry for which a description is to
be created, and the database that contains the entry."
:group 'ebib
:type '(choice (function-item :tag "Author/Year" ebib-create-org-author/year)
(function-item :tag "Title" ebib-create-org-title)
(function-item :tag "Author/Year/Title" ebib-create-org-description)
(function :tag "Custom Function")))
(defcustom ebib-multiline-major-mode 'text-mode
"The major mode of the multiline edit buffer."
:group 'ebib
:type '(function :tag "Mode function"))
(defcustom ebib-multiline-display-function 'ebib-multiline-display-as-is
"The way in which multiline field values are shown in the index buffer.
By default, the value is shown as-is, but never more than
`ebib-multiline-display-max-lines' lines. The option \"First
Paragraph\" displays the first paragraph (also with a maximum of
`ebib-multiline-display-max-lines' lines), but in addition
refills the text, which is useful if you use long lines in your
multiline values.
It is possible to provide a custom function for this option.
This function should take a (multiline) string as argument and
return a list of lines to be displayed."
:group 'ebib
:type '(choice (function-item :tag "Show Value As-Is" ebib-multiline-display-as-is)
(function-item :tag "Show First Paragraph" ebib-multiline-display-paragraph)
(function :tag "Custom Function")))
(defun ebib-multiline-display-as-is (string)
"Reduce the multiline text STRING.
The text is split into lines and returned. No other
modifications are made."
(split-string string "\n"))
(defun ebib-multiline-display-paragraph (string)
"Reduce the multiline text STRING.
The text is filled to account for the possibility that the
original text is unfilled. Return value is a list of strings,
each a single line."
(split-string (with-temp-buffer
(insert string)
(goto-char (point-min))
(forward-paragraph)
(delete-region (point) (point-max))
(fill-region (point-min) (point-max))
(buffer-string))
"\n" t))
(defcustom ebib-multiline-display-max-lines 10
"The maximum number of lines to display for multiline field values."
:group 'ebib
:type 'integer)
(defcustom ebib-sort-order nil
"The fields on which the BibTeX entries are to be sorted in the BibTeX file.
This option is described in the manual/info file in the section
\"Sorting the .bib file\"."
:group 'ebib
:type '(repeat (repeat :tag "Sort level" (string :tag "Sort field"))))
;; Entry type and field aliases defined by Biblatex.
(defconst ebib--field-aliases '(("location" . "address")
("annotation" . "annote")
("eprinttype" . "archiveprefix")
("journaltitle" . "journal")
("sortkey" . "key")
("file" . "pdf")
("eprintclass" . "primaryclass")
("institution" . "school"))
"List of field aliases for Biblatex.")
(defconst ebib--type-aliases '(("Conference" . "InProceedings")
("Electronic" . "Online")
("MastersThesis" . "Thesis")
("PhDThesis" . "Thesis")
("TechReport" . "Report")
("WWW" . "Online"))
"List of entry type aliases for Biblatex.")
(defcustom ebib-bibtex-dialect 'BibTeX
"The default BibTeX dialect.
A `.bib' file/database without explicit dialect setting is
assumed to use this dialect. Possible values are those listed in
`bibtex-dialect-list'."
:group 'ebib
:type `(choice :tag "BibTeX Dialect"
,@(mapcar (lambda (d) `(const ,d))
bibtex-dialect-list)))
(defcustom ebib-biblatex-inheritances '(;; Source Target
("all" "all"
(("ids" . none)
("crossref" . none)
("xdata" . none)
("xref" . none)
("entryset" . none)
("entrysubtype" . none)
("execute" . none)
("label" . none)
("options" . none)
("presort" . none)
("related" . none)
("relatedoptions" . none)
("relatedstring" . none)
("relatedtype" . none)
("shorthand" . none)
("shorthandintro" . none)
("sortkey" . none)))
;; Source Target
("mvbook, book" "inbook, bookinbook, suppbook"
(("author" . "author")
("author" . "bookauthor")))
;; Source Target
("mvbook" "book, inbook, bookinbook, suppbook"
(("title" . "maintitle")
("subtitle" . "mainsubtitle")
("titleaddon" . "maintitleaddon")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none)))
;; Source Target
("mvcollection, mvreference" "collection, reference, incollection, inreference, suppcollection"
(("title" . "maintitle")
("subtitle" . "mainsubtitle")
("titleaddon" . "maintitleaddon")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none)))
;; Source Target
("mvproceedings" "proceedings, inproceedings"
(("title" . "maintitle")
("subtitle" . "mainsubtitle")
("titleaddon" . "maintitleaddon")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none)))
;; Source Target
("book" "inbook, bookinbook, suppbook"
(("title" . "booktitle")
("subtitle" . "booksubtitle")
("titleaddon" . "booktitleaddon")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none)))
;; Source Target
("collection, reference" "incollection, inreference, suppcollection"
(("title" . "booktitle")
("subtitle" . "booksubtitle")
("titleaddon" . "booktitleaddon")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none)))
;; Source Target
("proceedings" "inproceedings"
(("title" . "booktitle")
("subtitle" . "booksubtitle")
("titleaddon" . "booktitleaddon")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none)))
;; Source Target
("periodical" "article, suppperiodical"
(("title" . "journaltitle")
("subtitle" . "journalsubtitle")
("shorttitle" . none)
("sorttitle" . none)
("indextitle" . none)
("indexsorttitle" . none))))
"Inheritance scheme for cross-referencing.
This option defines inheritances for BibLaTeX. Inheritances are
specified for pairs of source and target entry type, where the
source is the cross-referenced entry and the target the
cross-referencing entry. For each pair, specify the fields in
the source and the fields in the target that they correspond
with.
Inheritances valid for all entry types can be defined by
specifying \"all\" as the entry type. Entry types (both source
and target) may also be a (comma-separated) list of entry types.
If no inheritance rule is set up for a given entry type+field
combination, the field inherits from the same-name field in the
cross-referenced entry. If no inheritance should take place, set
the target field to \"No inheritance\".
All entries made in this option should be in lower case.
Note that this option is only relevant for BibLaTeX. If the
BibTeX dialect is set to `BibTeX', this option is ignored."
:group 'ebib
:type '(repeat (list (string :tag "Source entry type(s)")
(string :tag "Target entry type(s)")
(repeat (cons :tag "Inheritance"
(string :tag "Source field")
(choice (string :tag "Target field)")
(const :tag "No inheritance" none)))))))
(defcustom ebib-follow-current-field-crossref t
"Consider only current crossref fields when following crossrefs.
If non-nil, `ebib-follow-crossref' will only consider keys
present in the value of the current field, when called with point
on a field which crossrefs other entries (`crossref', `xdata' or
`xref')."
:group 'ebib
:type 'boolean)
(defcustom ebib-save-xrefs-first t
"Save entries with a crossref field first in the BibTeX-file.
For BibTeX's cross-referencing to work, the cross-referencing
entries must appear in the `.bib` file before the
cross-referenced entries. This option tells Ebib to save all
entries with a `crossref` field first, so that BibTeX's
crossreferencing options work as intended.
Note: this option is not compatible with setting the option
`ebib-sort-order'. If you want to use the latter, unset this
one."
:group 'ebib
:type 'boolean)
(defcustom ebib-save-indent-as-bibtex nil
"If non-nil, use `bibtex-mode' variables for indentation when saving.
By default, when saving a `.bib' file, Ebib uses a single TAB
character to indent fields. When this option is set, Ebib uses
the values of the variables `bibtex-entry-offset' and
`bibtex-field-indentation' to compute the indentation and indents
using spaces."
:group 'ebib
:type 'boolean)
(defcustom ebib-use-timestamp nil
"Add a timestamp to new entries.
If this option is set, Ebib will add a `timestamp` field to every
new entry, recording the date and time it was added to the
database. See the section \"Timestamps\" in the manual/info file for
details.
Note that the `timestamp' field is normally hidden. You can make
it visible with \\[ebib--toggle-hidden] in the index buffer or by
customizing the option `ebib--hidden-fields'."
:group 'ebib
:type 'boolean)
(defcustom ebib-timestamp-format "%Y-%m-%d %T (%Z)"
"Format of the time string used in the timestamp.
This option specifies the format string that is used to create
the timestamp. The default value produces a timestamp of the
form \"2007-03-12 01:03:26 (CET)\". This option uses the Emacs
function `format-time-string` to create the timestamp. See that
function's documentation for details on customizing the format
string."
:group 'ebib
:type 'string)
(make-obsolete-variable 'ebib-url-field "The default URL field can no longer be customized" "Ebib 2.27")
(defcustom ebib-url-regexp "\\\\url{\\(?1:.*\\)}\\|\\(?1:https?://[^ ';<>\"\n\t\f]+\\)"
"Regular expression to extract URLs from a field.
This is the regular expression that Ebib uses to search for URLs
in a field. With the default value, Ebib considers everything
that is in a LaTeX \"\\url{...}\" command as a URL, and
furthermore every string of text that starts with \"http://\" or
\"https://\" and does not contain whitespace or one of the
characters ' \" ; < or >.
The semicolon is included in this set for consistency: it makes
it possible to use the same separator in the `url' field as in
the `file' field. Note that this is not a necessity: URLs can be
separated on whitespace.
If you customize this option, make sure to use an explicitly
numbered group to enclose the actual URL, since this is what the
function `ebib--split-urls' expects."
:group 'ebib
:type 'string)
(defcustom ebib-url-download-transformations '(("https?://arxiv.org/abs/" . ebib-transform-arXiv-url)
("https?://ling.auf.net/lingBuzz/" . ebib-transform-lingbuzz-url)
("https?://www.jstor.org/" . ebib-transform-jstor-url)
("https?://\\(www.\\)?aclweb.org/anthology/" . ebib-transform-aclanthology-url)
("https?://link.aps.org/" . ebib-transform-aps-url))
"Transformations to apply to a URL before attempting to download a pdf file.
Each entry consists of a matcher and a transformation function.
The matcher is a regular expression that is matched (with
`string-match-p') against a given URL. If the URL matches, the
transformation function is applied to the URL."
:group 'ebib
:type '(repeat (cons :tag "Transformation"
(regexp :tag "Matcher") (function :tag "Function"))))
(defun ebib-transform-arXiv-url (url)
"Transform an arXiv URL to the URL of its correspnoding pdf file."
(concat (replace-regexp-in-string (regexp-quote "/abs/") "/pdf/" url t t) ".pdf"))
(defun ebib-transform-lingbuzz-url (url)
"Transform a lingbuzz URL to the URL of its corresponding pdf file."
(concat url "/current.pdf"))
(defun ebib-transform-jstor-url (url)
"Transfrom a JSTOR URL to the URL of its corresponding pdf file."
(save-match-data
(string-match "\\(https?://www.jstor.org/stable/\\)\\([0-9]*\\)" url)
(format "%spdf/%s.pdf" (match-string 1 url) (match-string 2 url))))
(defun ebib-transform-aps-url (url)
"Transform an APS URL to the URL of its corresponding pdf file."
(save-match-data
(cond
((string-match (regexp-quote "PhysRevA") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/pra/pdf/" url t t))
((string-match (regexp-quote "PhysRevB") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prb/pdf/" url t t))
((string-match (regexp-quote "PhysRevC") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prc/pdf/" url t t))
((string-match (regexp-quote "PhysRevD") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prd/pdf/" url t t))
((string-match (regexp-quote "PhysRevE") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/pre/pdf/" url t t))
((string-match (regexp-quote "PhysRevFluids") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prfluids/pdf/" url t t))
((string-match (regexp-quote "PhysRevLett") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prl/pdf/" url t t))
((string-match (regexp-quote "PhysRevAccelBeams") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prab/pdf/" url t t))
((string-match (regexp-quote "PhysRevApplied") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prapplied/pdf/" url t t))
((string-match (regexp-quote "PhysRevMaterials") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prmaterials/pdf/" url t t))
((string-match (regexp-quote "PhysRevResearch") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prresearch/pdf/" url t t))
((string-match (regexp-quote "PhysRevX") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prx/pdf/" url t t))
((string-match (regexp-quote "RevModPhys") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/rmp/pdf/" url t t))
((string-match (regexp-quote "PhysRevPhysEducRes") url)
(replace-regexp-in-string (regexp-quote "/link.aps.org/doi/") "/journals.aps.org/prper/pdf/" url t t)))))
(defun ebib-transform-aclanthology-url (url)
"Transform an ACL anthology URL to the URL of its correspnoding pdf file."
(if (string-match-p "\\.pdf$" url)
url
(concat (string-remove-suffix "/" url) ".pdf")))
(defcustom ebib-browser-command nil
"Command to call the browser with.
If this option is unset, Ebib uses the Emacs function
`browse-url' to start a browser. If you prefer not to use this,
you can set this option to the executable name of your preferred
browser. For this to work, the browser that you use must be able
to handle a URL on the command line."
:group 'ebib
:type '(choice (const :tag "Use standard browser" nil)
(string :tag "Specify browser command")))
(defcustom ebib-doi-resolver "https://doi.org/"
"URL to use as a DOI resolver for the \"doi\" field.
By default, use the DOI Foundation's preferred proxy,
https://doi.org/. Note that this URL must end in a slash."
:group 'ebib
:type '(string :tag "DOI resolver"
:validate (lambda (widget)
(unless (string-suffix-p "/" (widget-value widget))
(widget-put widget :error "[Ebib] DOI resolver must end in a slash.")
widget))))
(make-obsolete-variable 'ebib-doi-field "The default DOI field can no longer be customized" "Ebib 2.27")
(make-obsolete-variable 'ebib-file-field "The standard file field can no longer be customized" "Ebib 2.27")
(defcustom ebib-import-source-directory "~/Downloads/"
"Directory to import files from.
When using the command `ebib-import-file', this directory is
offered as the default directory to import a file from."
:group 'ebib
:type 'directory)
(make-obsolete-variable 'ebib-import-directory 'ebib-import-source-directory "Ebib 2.41")
(defcustom ebib-import-target-directory 'first-search-dir
"Directory to save imported files to.
Valid values are the symbol `first-search-dir', which means to
use the first directory in `ebib-file-search-dirs', the symbol
`current-db', which means to use the directory of the current
database, the symbol `ask', which means to ask the user each
time, or a directory path (as a string)."
:group 'ebib
:type '(choice (const :tag "Use first directory in `ebib-file-search-dirs'" first-search-dir)