Skip to content

Commit da5f0c5

Browse files
committed
Generalize notes
Refactor the notes API to enable more general functionality (see #518), and tighter integration with note-taking packages like org-roam (see #602). - Add citar-keys-with-notes-functions defcustom. - Rename citar-open-notes-function to citar-open-notes-functions; make a list. - Refactor citar-open-notes to make use of the above - Add some convenience functions for working with these data. Close #601
1 parent 7dd4a21 commit da5f0c5

File tree

3 files changed

+89
-23
lines changed

3 files changed

+89
-23
lines changed

README.org

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,15 +267,21 @@ For additional configuration options on this, see [[https://github.com/bdarcus/c
267267

268268
** Notes
269269

270-
Citar provides a ~citar-format-note-function~ variable, and a default function for org, which also works well with org-roam (v2 now supports org-cite).
270+
Citar provides a ~citar-create-note-function~ variable, and a default function for org, which also works well with org-roam (v2 now supports org-cite).
271271
You can configure the title display using the "note" template.
272272

273-
You can also use the ~citar-open-note-function~ variable to replace the default with another; for example from org-roam-bibtex:
273+
You can also use the ~citar-open-note-functions~ variable to replace or augment the default with another; for example from org-roam-bibtex:
274274

275275
#+BEGIN_SRC emacs-lisp
276-
(setq citar-open-note-function 'orb-citar-edit-note)
276+
(setq citar-open-note-functions '(orb-citar-edit-note))
277277
#+END_SRC
278278

279+
Since ~citar-open-note-functions~ is a list, you can also include multiple functions, to handle different note scenarios.
280+
281+
Citar also includes a ~citar-keys-with-notes-functions~ variable, which specifies a list of functions, each of which returns a list of keys that have associated notes.
282+
This function allows Citar to correctly format the completion UI candidates.
283+
The default function only supports one-file-per-key notes.
284+
279285
** Files, file association and file-field parsing
280286

281287
If you have ~citar-library-paths~ set, the relevant open commands will look in those directories for file names of =CITEKEY.EXTENSION=.

citar-file.el

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
(require 'cl-lib)
2828
(require 'subr-x))
2929
(require 'seq)
30+
(make-obsolete 'citar-format-note-function 'citar-create-note-function "1.0")
3031

3132
;;; pre-1.0 API cleanup
3233

@@ -93,6 +94,9 @@ separator that does not otherwise occur in citation keys."
9394
(regexp :tag "Filename separator")))
9495

9596
(defvar citar-notes-paths)
97+
(defvar citar-create-note-function)
98+
(defvar citar-library-paths)
99+
(defvar citar-library-file-extensions)
96100

97101
;;;; Convenience functions for files and paths
98102

@@ -229,6 +233,26 @@ need to scan the contents of DIRS in this case."
229233
(puthash key (nreverse filelist) files))
230234
files))))
231235

236+
(defun citar-file--has-file-notes-hash ()
237+
"Return a hash of keys and file paths for notes."
238+
(citar-file--directory-files
239+
citar-notes-paths nil citar-file-note-extensions
240+
citar-file-additional-files-separator))
241+
242+
(defun citar-file--has-library-files-hash ()
243+
"Return a hash of keys and file paths for library files."
244+
(citar-file--directory-files
245+
citar-library-paths nil citar-library-file-extensions
246+
citar-file-additional-files-separator))
247+
248+
(defun citar-file--keys-with-file-notes ()
249+
"Return a list of keys with file notes."
250+
(hash-table-keys (citar-file--has-file-notes-hash)))
251+
252+
(defun citar-file--keys-with-library-files ()
253+
"Return a list of keys with file notes."
254+
(hash-table-keys (citar-file--has-library-files-hash)))
255+
232256
(defun citar-file--has-file (dirs extensions &optional entry-field)
233257
"Return predicate testing whether a key and entry have associated files.
234258
@@ -322,6 +346,19 @@ of files found in two ways:
322346
nil 0 nil
323347
file)))
324348

349+
(defun citar-file--open-note (key entry)
350+
"Open a note file from KEY and ENTRY."
351+
(if-let* ((file (citar-file--get-note-filename key
352+
citar-notes-paths
353+
citar-file-note-extensions))
354+
(file-exists (file-exists-p file)))
355+
(find-file file)
356+
(if (and (null citar-notes-paths)
357+
(equal citar-create-note-function
358+
'citar-org-format-note-default))
359+
(error "You must set 'citar-notes-paths'")
360+
(funcall citar-create-note-function key entry file))))
361+
325362
(defun citar-file--get-note-filename (key dirs extensions)
326363
"Return existing or new filename for KEY in DIRS with extension in EXTENSIONS.
327364

citar.el

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@
6262

6363
;; also rename
6464
(make-obsolete 'citar-has-a-value 'citar--field-with-value "1.0")
65+
(make-obsolete 'citar--open-note 'citar-file--open-note "1.0")
6566

66-
67+
(make-obsolete-variable
68+
'citar-format-note-function 'citar-create-note-function "1.0")
6769

6870
;;; Declare variables and functions for byte compiler
6971

@@ -222,6 +224,20 @@ If nil, single resources will open without prompting."
222224
:group 'citar
223225
:type '(boolean))
224226

227+
;;; Note-handling setup
228+
229+
(defcustom citar-open-note-functions
230+
'(citar-file--open-note)
231+
"List of functions to open a note."
232+
:group 'citar
233+
:type '(function))
234+
235+
(defcustom citar-keys-with-notes-functions
236+
'(citar-file--keys-with-file-notes)
237+
"Functions, without argument, to return a list of keys with notes."
238+
:group 'citar
239+
:type '(function))
240+
225241
(defcustom citar-open-note-function
226242
'citar--open-note
227243
"Function to open a new or existing note.
@@ -233,9 +249,9 @@ ENTRY: an alist with the structured data (title, author, etc.)"
233249
:group 'citar
234250
:type 'function)
235251

236-
(defcustom citar-format-note-function
252+
(defcustom citar-create-note-function
237253
'citar-org-format-note-default
238-
"Function to format a new note.
254+
"Function to create a new note.
239255
240256
A note function must take three arguments:
241257
@@ -475,6 +491,14 @@ REBUILD-CACHE and FILTER."
475491
(category . multi-category))
476492
(complete-with-action action (delete-dups resources) string predicate))))))
477493

494+
(defun citar--keys-with-notes ()
495+
"Return a list of keys with associated notes."
496+
(seq-uniq
497+
(seq-mapcat
498+
(lambda (fn)
499+
(funcall fn))
500+
citar-keys-with-notes-functions)))
501+
478502
(defun citar--select-group-related-resources (resource transform)
479503
"Group RESOURCE by type or TRANSFORM."
480504
(let ((extension (file-name-extension resource)))
@@ -605,6 +629,7 @@ repeatedly."
605629
(citar-file--has-file citar-notes-paths
606630
citar-file-note-extensions))
607631

632+
608633
(defun citar--format-candidates (bib-files &optional context)
609634
"Format candidates from BIB-FILES, with optional hidden CONTEXT metadata.
610635
This both propertizes the candidates for display, and grabs the
@@ -613,15 +638,15 @@ key associated with each one."
613638
(raw-candidates
614639
(parsebib-parse bib-files :fields (citar--fields-to-parse)))
615640
(hasfilep (citar-has-file))
616-
(hasnotep (citar-has-note))
641+
(hasnote (citar--keys-with-notes))
617642
(main-width (citar--format-width (citar--get-template 'main)))
618643
(suffix-width (citar--format-width (citar--get-template 'suffix)))
619644
(symbols-width (string-width (citar--symbols-string t t t)))
620645
(star-width (- (frame-width) (+ 2 symbols-width main-width suffix-width))))
621646
(maphash
622647
(lambda (citekey entry)
623648
(let* ((files (when (funcall hasfilep citekey entry) " has:files"))
624-
(notes (when (funcall hasnotep citekey entry) " has:notes"))
649+
(notes (when (member citekey hasnote) " has:notes"))
625650
(link (when (citar--field-with-value '("doi" "url") entry) "has:link"))
626651
(candidate-main
627652
(citar--format-entry
@@ -1041,21 +1066,19 @@ With prefix, rebuild the cache before offering candidates."
10411066
With prefix, rebuild the cache before offering candidates."
10421067
(interactive (list (citar-select-ref
10431068
:rebuild-cache current-prefix-arg)))
1044-
(let ((embark-default-action-overrides '((file . find-file))))
1045-
(when (and (null citar-notes-paths)
1046-
(equal citar-format-note-function
1047-
'citar-org-format-note-default))
1048-
(error "You must set 'citar-notes-paths' to open notes with default notes function"))
1049-
(funcall citar-open-note-function (car key-entry) (cdr key-entry))))
1050-
1051-
(defun citar--open-note (key entry)
1052-
"Open a note file from KEY and ENTRY."
1053-
(if-let* ((file (citar-file--get-note-filename key
1054-
citar-notes-paths
1055-
citar-file-note-extensions))
1056-
(file-exists (file-exists-p file)))
1057-
(find-file file)
1058-
(funcall citar-format-note-function key entry file)))
1069+
(let* ((embark-default-action-overrides '((file . find-file)))
1070+
(key (car key-entry))
1071+
(entry (cdr key-entry)))
1072+
(if (listp citar-open-note-functions)
1073+
(citar--open-notes key entry)
1074+
(error "Please change the value of 'citar-open-note-functions' to a list"))))
1075+
1076+
(defun citar--open-notes (key entry)
1077+
"Open note(s) associated with KEY and ENTRY."
1078+
(or (seq-some
1079+
(lambda (opener)
1080+
(funcall opener key entry)) citar-open-note-functions)
1081+
(funcall citar-create-note-function key entry)))
10591082

10601083
;;;###autoload
10611084
(defun citar-open-entry (key-entry)

0 commit comments

Comments
 (0)