0

I want to org-capture (or something like it) to a non org file, in particular a bibtex file.

In more detail

If point is in an org-file in a link of the shape [myurl][mydescription] And I call org-capture, I want

  • a prompt for a key
  • An entry of the shape
  @online{prompted-key
    url   = {myurl};
    title = {mydescription}
}

to get appended to some designated .bib file

1 Answer 1

1

AFAICS, there is no problem capturing into any file you want. You just need the appropriate capture template. From C-h v org-capture-templates, we can see that the type of the template should be plain: that will add literal text into any file you want:

(setq my/bibtex-template  '("B" "Bibtex template" plain
                                 (file "/tmp/foo.bib")
                                 "@online{prompted-key}
     url   = {myurl};
     title = {mydescription}
 }"
                                 :prepend t))

If you add this template to org-capture-templates:

(add-to-list 'org-capture-templates my/bibtex-template)

and do a capture with key B, you'll see that the text ends up in the specified file, /tmp/foo.bib. Of course, we are not quite done: we need to prompt the user for the key and myurl and mydescription need to be replace by the "real" values.

Now the doc string of org-capture-templates also tells you how to prompt:

...
  %^{prompt}  Prompt the user for a string and replace this sequence with it.
              A default value and a completion table can be specified like this:
              %^{prompt|default|completion2|completion3|...}.
...

so the template can easily be modified to do that:

(setq my/bibtex-template  '("B" "Bibtex template" plain
                                 (file "/tmp/foo.bib")
                                 "@online{%^{Key}}
     url   = {myurl};
     title = {mydescription}
 }"
                                 :prepend t))

We still have the wrong URL and description, but the prompting for the key works fine.

Getting the correct values into the file has two parts: we have to get the values out of the link and we have to pass them to the capture template. IOW, we need a dynamic template. So we'll write a function that gets the values out of the link and that then constructs the template on the run, filling in the values it got; it will then invoke org-capture appropriately to pick up the constructed template. Here's how that looks:

(defun my/bibtex-link-capture ()
  (interactive)
  (let* ((link (org-element-lineage (org-element-context) '(link) t))
         (myurl (my/org-link-get-url link))
         (mydescription (my/org-link-get-description link))
         (my-bibtex-template `("B" "Bibtex template" plain
                               (file "/tmp/foo.bib")
                               ,(concat "@online{%^{Key}}
      url   = {" myurl "};
      title = {" mydescription"}
  }")
                               :prepend t))
         (org-capture-templates (list my-bibtex-template)))
    (org-capture nil "B")))

The function uses the org-element parser to get the parsed representation of the link, and then calls two functions, one to get the URL and one to get the description out of the (parsed representation of the) link. It then constructs the template but interpolates the values of the URL and the description it just got; it then adds the template to a local, let-bound version of org-capture-templates (which shadows the global value that you probably have now - note that the local value contains just one template, the one it just constructed) and calls org-capture telling it to use the "B" template.

The only other thing that is needed is the two functions that get the URL and the description out of the link:

(defun my/org-link-get-url (link)
  "Extract and return the URL (type:path) of the given link"
  (concat (org-element-property :type link) ":" (org-element-property :path link)))

(defun my/org-link-get-description (link)
  "Extract and return the description of the given link"
  (buffer-substring-no-properties (org-element-property :contents-begin link)
                    (org-element-property :contents-end link)))

The parser represents a link as an association list with a bunch of properties (:type, :path, :contents-begin, :contents-end, etc.), so all that these functions have to do is retrieve the values of these properties and use them to construct and return what they are supposed to. In the URL case, the type is something like https and the path is the resource path, so all we have to do is concatenate them with a : in-between. In the description case, the :contents-begin and :contents-end properties are positions in the buffer, so we use them to get the buffer substring between those positions.

If you put the three functions in the two code blocks above into a file, foo.el, and load that file, then you can position the cursor on some link, e.g:

[[https://emacs.stackexchange.com/questions/80734/org-capture-to-bibtex-file][My perfect link]]

and say M-x my/bibtex-link-capture, type in the key at the prompt and press C-c C-c to finish the capture. I get a warning Warning (org-element): ‘org-element-at-point’ cannot be used in non-Org buffer #<buffer foo.bib> (bibtex-mode), but the entry does appear in the file correctly. I haven't traced the responsible party to try to eliminate the warning, but I think you can disregard it.

1
  • V kind of you Nick!
    – Rusi
    Commented Mar 17 at 2:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.