diff --git a/citar-file.el b/citar-file.el index 1a1c74d9..82a73094 100644 --- a/citar-file.el +++ b/citar-file.el @@ -296,7 +296,7 @@ of files found in two ways: (defun citar-file-open (file) "Open FILE." - (funcall citar-file-open-function file)) + (funcall citar-file-open-function (expand-file-name file))) (defun citar-file-open-external (file) "Open FILE with external application." @@ -311,20 +311,18 @@ of files found in two ways: nil 0 nil file))) -(defun citar-file--get-note-filename (key dirs extensions) - "Return existing or new filename for KEY in DIRS with extension in EXTENSIONS. - -This is for use in a note function where notes are one-per-file, -with citekey as filename. - -Returns the filename whether or not the file exists, to support a -function that will open a new file if the note is not present." - (let ((files (citar-file--directory-files dirs (list key) extensions - citar-file-additional-files-separator))) - (or (car (gethash key files)) - (when-let ((dir (car dirs)) - (ext (car extensions))) - (expand-file-name (concat key "." ext) dir))))) +(defun citar-file--get-note-filenames (key dirs extensions) + "Return list of existing notes or a new filename for KEY in DIRS with extension in EXTENSIONS. + +If no notes exist, returns a filename to support a function that +will open a new file if the note is not present." + (if-let* ((files + (gethash key (citar-file--directory-files dirs + (list key) + extensions + citar-file-additional-files-separator)))) + files + (list (concat (car dirs) "/" key "." (car extensions))))) (provide 'citar-file) ;;; citar-file.el ends here diff --git a/citar.el b/citar.el index 5f7abc9d..1bcb7bf8 100644 --- a/citar.el +++ b/citar.el @@ -55,6 +55,7 @@ (defvar embark-meta-map) (defvar embark-transformer-alist) (defvar embark-multitarget-actions) +(defvar embark-default-action-overrides) (defvar citar-org-open-note-function) ;;; Variables @@ -419,26 +420,37 @@ documentation for the return value and the meaning of REBUILD-CACHE and FILTER." (citar-select-ref :rebuild-cache rebuild-cache :multiple t :filter filter)) -(defun citar-select-files (files) - "Select file(s) from a list of FILES." - ;; TODO add links to candidates - (completing-read-multiple - "Open related file(s): " - (lambda (string predicate action) - (if (eq action 'metadata) - `(metadata - ; (group-function . citar-select-group-related-sources) - (category . file)) - (complete-with-action action files string predicate))))) - -(defun citar-select-group-related-sources (file transform) - "Group by FILE by source, TRANSFORM." - (let ((extension (file-name-extension file))) - (when transform file - ;; Transform for grouping and group title display. +(defun citar-select-resource (files &optional links) + "Select resource from a list of FILES, and optionally LINKS." + (let* ((files (mapcar + (lambda (cand) + (abbreviate-file-name cand)) + files)) + (resources (append files (remq nil links)))) + (dolist (item resources) + (cond ((string-match "http" item 0) + (add-text-properties 0 (length item) `(consult-multi (url . ,item)) item)) + (t + (add-text-properties 0 (length item) `(consult-multi (file . ,item)) item))) + (push item resources)) + (completing-read + "Select resource: " + (lambda (string predicate action) + (if (eq action 'metadata) + `(metadata + (group-function . citar-select-group-related-resources) + (category . consult-multi)) + (complete-with-action action (delete-dups resources) string predicate)))))) + +(defun citar-select-group-related-resources (resource transform) + "Group RESOURCE by type or TRANSFORM." + (let ((extension (file-name-extension resource))) + (if transform + resource (cond - ((string= extension (or "org" "md")) "Notes") - (t "Library Files"))))) + ((member extension citar-file-note-extensions) "Notes") + ((string-match "http" resource 0) "Links") + (t "Library Files"))))) (defun citar--get-major-mode-function (key &optional default) "Return KEY from 'major-mode-functions'." @@ -885,13 +897,16 @@ into the corresponding reference key. Return ;;;###autoload (defun citar-open (keys-entries) - "Open related resources (links or files) for KEYS-ENTRIES." + "Open any related resource (links, files, or notes) for KEYS-ENTRIES." (interactive (list (citar-select-refs :rebuild-cache current-prefix-arg))) (when (and citar-library-paths (stringp citar-library-paths)) (message "Make sure 'citar-library-paths' is a list of paths")) - (let* ((key-entry-alist (citar--ensure-entries keys-entries)) + (let* ((embark-default-action-overrides '((consult-multi . citar-open-multi) + (file . citar-file-open) + (url . browse-url))) + (key-entry-alist (citar--ensure-entries keys-entries)) (files (citar-file--files-for-multiple-entries key-entry-alist @@ -903,17 +918,21 @@ into the corresponding reference key. Return (lambda (key-entry) (citar-get-link (cdr key-entry))) key-entry-alist)) - (resource-candidates (delete-dups (append files (remq nil links)))) - (resources - (when resource-candidates - (completing-read-multiple "Related resources: " resource-candidates)))) - (if resource-candidates - (dolist (resource resources) - (cond ((string-match "http" resource 0) - (browse-url resource)) - (t (citar-file-open resource)))) + (selection (citar-select-resource files links))) + (if files + (cond ((string-match "http" selection 0) + (browse-url selection)) + (t (citar-file-open selection))) (message "No associated resources")))) +(defun citar-open-multi (selection) + "Act appropriately on SELECTION when type is 'consult-multi'. + +For use with 'embark-act-all'." + (cond ((string-match "http" selection 0) + (browse-url selection)) + (t (citar-file-open selection)))) + (defun citar--library-files-action (keys-entries action) "Run ACTION on files associated with KEYS-ENTRIES." (if-let ((fn (pcase action @@ -926,12 +945,9 @@ into the corresponding reference key. Return citar-file-extensions))) (if (and citar-file-open-prompt (> (length files) 1)) - (let ((selected-files - (citar-select-files files))) - (dolist (file selected-files) - (funcall fn file))) - (dolist (file files) - (funcall fn file))) + (let ((selection (citar-select-resource files))) + (funcall fn selection)) + (funcall fn (car files))) (message "No associated file"))) ;;;###autoload @@ -941,10 +957,11 @@ into the corresponding reference key. Return With prefix, rebuild the cache before offering candidates." (interactive (list (citar-select-refs :rebuild-cache current-prefix-arg))) + (let ((embark-default-action-overrides '((file . citar-file-open)))) (when (and citar-library-paths (stringp citar-library-paths)) (message "Make sure 'citar-library-paths' is a list of paths")) - (citar--library-files-action keys-entries 'open)) + (citar--library-files-action keys-entries 'open))) (make-obsolete 'citar-open-pdf 'citar-open-library-files "1.0") @@ -955,21 +972,31 @@ With prefix, rebuild the cache before offering candidates." With prefix, rebuild the cache before offering candidates." (interactive (list (citar-select-refs :rebuild-cache current-prefix-arg))) - (when (and (null citar-notes-paths) - (equal citar-format-note-function - 'citar-org-format-note-default)) - (error "You must set 'citar-notes-paths' to open notes with default notes function")) - (dolist (key-entry (citar--ensure-entries keys-entries)) - (funcall citar-open-note-function (car key-entry) (cdr key-entry)))) + (let ((embark-default-action-overrides '((file . find-file)))) + (when (and (null citar-notes-paths) + (equal citar-format-note-function + 'citar-org-format-note-default)) + (error "You must set 'citar-notes-paths' to open notes with default notes function")) + (dolist (key-entry (citar--ensure-entries keys-entries)) + (funcall citar-open-note-function (car key-entry) (cdr key-entry))))) (defun citar--open-note (key entry) "Open a note file from KEY and ENTRY." - (if-let* ((file (citar-file--get-note-filename key - citar-notes-paths - citar-file-note-extensions)) - (file-exists (file-exists-p file))) - (find-file file) - (funcall citar-format-note-function key entry file))) + (if-let* ((raw-files (citar-file--get-note-filenames key + citar-notes-paths + citar-file-note-extensions)) + (files + (mapcar + (lambda (file) + (abbreviate-file-name file)) + raw-files)) + (file (when (= (length files) 1) + (car files)))) + (if (file-exists-p file) + (find-file (expand-file-name file)) + (when (yes-or-no-p (format "No note associated with %s. Create one?" key)) + (funcall citar-format-note-function key entry (expand-file-name file)))) + (find-file (citar-select-resource files)))) ;;;###autoload (defun citar-open-entry (keys-entries) @@ -1031,15 +1058,14 @@ directory as current buffer." "Open URL or DOI link associated with the KEYS-ENTRIES in a browser. With prefix, rebuild the cache before offering candidates." - ;; (browse-url-default-browser "https://google.com") (interactive (list (citar-select-refs :rebuild-cache current-prefix-arg))) - (dolist (key-entry (citar--ensure-entries keys-entries)) - (let ((link (citar-get-link (cdr key-entry)))) - (if link - (browse-url-default-browser link) - (message "No link found for %s" (car key-entry)))))) - + (let ((embark-default-action-overrides '((url . browse-url)))) + (dolist (key-entry (citar--ensure-entries keys-entries)) + (let ((link (citar-get-link (cdr key-entry)))) + (if link + (browse-url link) + (message "No link found for %s" (car key-entry))))))) ;;;###autoload (defun citar-insert-citation (keys-entries &optional arg) @@ -1118,10 +1144,11 @@ With prefix, rebuild the cache before offering candidates." With prefix, rebuild the cache before offering candidates." (interactive (list (citar-select-refs :rebuild-cache current-prefix-arg))) + (let ((embark-default-action-overrides '((file . mml-attach-file)))) (when (and citar-library-paths (stringp citar-library-paths)) (message "Make sure 'citar-library-paths' is a list of paths")) - (citar--library-files-action keys-entries 'attach)) + (citar--library-files-action keys-entries 'attach))) (make-obsolete 'citar-add-pdf-attachment 'citar-attach-library-files "0.9")