emacs-citar / citar

Emacs package to quickly find and act on bibliographic references, and edit org, markdown, and latex academic documents.
GNU General Public License v3.0
500 stars 54 forks source link

Add support for file links #259

Closed bdarcus closed 3 years ago

bdarcus commented 3 years ago

First of all, thanks for the great package. I have jumped into the vertico ecosystem mainly thanks to having a viable ivy-bibtex replacement and am generally having a great time. I can do nearly everything I want much more conveniently.

However I have been having trouble opening pdf files based on the file field in my bibtex entries and would like to know if this is currently supported or planned to be in the future.

To illustrate what I can't do, I have this entry in my bibliography file:

@book{albertyThermodynamicsBiochemicalReactions2003,
  title = {Thermodynamics of Biochemical Reactions},
  author = {Alberty, Robert A.},
  year = {2003},
  publisher = {{Wiley-Interscience}},
  address = {{Hoboken, N.J}},
  isbn = {978-0-471-22851-6},
  language = {en},
  lccn = {QP517.T48 A42 2003},
  keywords = {Bioenergetics,Physical biochemistry,Thermodynamics},
  file = {/Users/tedgro/Reading/Zotero/storage/LNAYX7S2/Alberty - 2003 - Thermodynamics of biochemical reactions.pdf}
}

When I run

(bibtex-actions-file-open-external
 (bibtex-actions-get-value
  "file"
  (bibtex-actions-get-entry "albertyThermodynamicsBiochemicalReactions2003")))

in the scratch buffer the pdf opens. However running either of these

(bibtex-actions-open '("albertyThermodynamicsBiochemicalReactions2003"))
(bibtex-actions-open-library-files '("albertyThermodynamicsBiochemicalReactions2003"))

produces the following message

"No file(s) found for (albertyThermodynamicsBiochemicalReactions2003)"

From a quick look at the code it seems like these both of these functions are designed around a workflow where the file names match the bibtex keys, which as you can see from the example above isn't how my files are organised (I think I just used the default settings for Zotero/better bibtex on macos for this).

I'd like to know if I have missed the right way to open pdfs based on the file field, or if this isn't currently supported whether it is planned in future.

For reference here is the relevant part of my init.el file:

(use-package bibtex-actions
  :ensure t
  :demand t
  :straight (:type git :host github :repo "bdarcus/bibtex-actions")
  :bind (("C-c b" . bibtex-actions-insert-citation)
         :map minibuffer-local-map
         ("M-b" . bibtex-actions-insert-preset))
  :after (embark parsebib bibtex-completion)
  :config
  (add-to-list 'embark-target-finders 'bibtex-actions-citation-key-at-point)
  (add-to-list 'embark-keymap-alist '(bib-reference . bibtex-actions-map))
  (add-to-list 'embark-keymap-alist '(citation-key . bibtex-actions-buffer-map))
  (advice-add #'completing-read-multiple
              :override #'consult-completing-read-multiple)
  (setq bibtex-actions-file-variable "file"
        bibtex-actions-bibliography '("/Users/tedgro/Reading/bibliography.bib")))

Originally posted by @teddygroves in https://github.com/bdarcus/bibtex-actions/discussions/258

bdarcus commented 3 years ago

I added this here, @teddygroves, as it's a legitimate feature request.

We don't currently support that, but happy to add it.

Basically, with #222 and some subsequent changes, we no longer use bibtex-completion for that functionality. So first step was what you see.

If you understand the current code, do you have thoughts on how best to extend it for this?

My understanding is the only wrinkle is non-standard field names?

teddygroves commented 3 years ago

If you understand the current code, do you have thoughts on how best to extend it for this?

I'm an elisp beginner (I basically just mess with my emacs config) so I can't say too much but I think bibtex-completion uses a variable called bibtex-completion-pdf-field and I noticed bibtex-actions already has a bibtex-actions-file-variable which I might be analogous?

bdarcus commented 3 years ago

@aikrahguzar - do you have any thoughts on where best to put the logic to pull the file filed value?

bibtex-actions-file--possible-names?

I could do it in the relevant "open" commands, but that feels wrong.

aikrahguzar commented 3 years ago

bibtex-actions-file--possible-names? Actually, that won't work.

It does seems like the obvious place for me. Though not entirely clear how to achieve that. One way would be for bibtex-actions-file--possible-names to take another (maybe optional) argument which is the value of the file field. It can than parse this string into file paths and add them to the list. I think another reason for this to happen in bibtex-actions-file--possible-names is that we should treat the resulting paths as just possible ones, they mightn't exist so they should go through the existence check as other paths.

Also we have to give users a way to supply the parsing function. Otherwise we will end up having to handle the quirks of every software generating bib files which is what BC does.

bdarcus commented 3 years ago

It does seems like the obvious place for me. Though not entirely clear how to achieve that.

In #260, I just look up the file value (using the file-variable field), and if present, push it to the result list of possible-names.

Also we have to give users a way to supply the parsing function.

We should itemize this somewhere at some point; maybe on the wiki? We really just need to know how parsebib parses the different options.

thisirs commented 3 years ago

Hi,

In bibtex-completion I have replaced the big cl-loop by a "hook until success" to work with my config like this :

(defvar bibtex-completion-find-pdf-hook
  '(bibtex-completion-find-pdf-simple
    bibtex-completion-find-pdf-zotero))

(defun bibtex-completion-find-pdf-simple (value)
  "Handle semicolon-separated relative or absolute filenames."
  (let* ((value (replace-regexp-in-string "\\([^\\]\\);" "\\1\^^" value))
         (items (s-split "\^^" value))
         (paths (-separate 'f-absolute-p items)))
    (append
     (--filter (and (f-file? it) (s-ends-with? ".pdf" it :ignore-case)) (car paths))
     (--filter (and (f-file? it) (s-ends-with? ".pdf" it :ignore-case))
               (-table-flat 'f-join (-flatten bibtex-completion-library-path)
                            (cadr paths))))))

(defun bibtex-completion-find-pdf-zotero (value)
  (let* ((value (replace-regexp-in-string "\\([^\\]\\);" "\\1\^^" value))
         (items (s-split "\^^" value)))
    (when (zerop (% (length items) 3))
      (--filter (and (f-file? it) (s-ends-with? ".pdf" it :ignore-case))
                (-map #'cadr (-partition 3 items))))))

(defun bibtex-completion-find-pdf-in-field (key-or-entry)
  "Return the path of the PDF specified in the field `bibtex-completion-pdf-field' if that file exists.
Returns nil if no file is specified, or if the specified file
does not exist, or if `bibtex-completion-pdf-field' is nil."
  (when bibtex-completion-pdf-field
    (let* ((entry (if (stringp key-or-entry)
                      (bibtex-completion-get-entry1 key-or-entry t)
                    key-or-entry))
           (value (bibtex-completion-get-value bibtex-completion-pdf-field entry)))
      (when value
        (run-hook-with-args-until-success 'bibtex-completion-find-pdf-hook value)))))

It allows the user to write his own parsing function or use predefined ones (Zotero, Jabref etc). Maybe we could do the same here?

PS: I end up here because I have a relative path to a pdf in the file fields of my bib file and this is not currently supported. :disappointed:

bdarcus commented 3 years ago

It allows the user to write his own parsing function or use predefined ones (Zotero, Jabref etc). Maybe we could do the same here?

I think this is something like what @aikrahguzar was thinking about, but am not sure.

What do your file fields look like, beyond being relative paths? Do the values differ significantly? If yes, what are examples? I see the "semicolon-separated" bit, but not sure what that means in context.

PS: I end up here because I have a relative path to a pdf in the file fields of my bib file and this is not currently supported.

This is I think just a minor oversight that should be easy to fix. Could you create an issue for it please?