Search code examples
emacsorg-mode

Getting current buffer directory from within org-capture


I'm currently using this template

(setq org-capture-templates
      `(("e" "ethz studies tasks"
         entry (file+function ,(concat org-directory "/ethz-tasks.org") (lambda ()
                                                                          (print (f-filename default-directory))
                                                                          (search-forward (f-filename default-directory))
                                                                          (move-end-of-line)
                                                                          (newline)))
         "** TODO %t [[file:%F][%f]]\n%?\n")))

In an attempt to get the template to file a little todo note about the current file, under a heading which is the name of that files parent directory

i.e.

* directory
** TODO <date> [[file:...][file]]
some extra info

The problem with my current snippet is that it uses default-directory, which in the context of the capture, seems to be "org". I want to get the name of the directory I was in before invoking the capture.

Which function should I be using for this ? Is there maybe some easier way to do this in org capture than the way I'm going about it ?


Solution

  • org-capture stores bits and pieces of information in a dynamically-scoped variable org-capture-plist. Once the capture process starts and creates the capture buffer, the global value is copied to a local buffer variable org-capture-current-plist. This is where the values for all the formats in the template are stored and are fetched from (%f, %F, etc).

    I have not tested any of this, so you might have to adjust according to what you find out, but I believe that you will need to query the local variable (although if you find that's nil, then you can try querying the global one). In either case, the original pathname is stored under the property :original-file, which you can get using

    (org-capture-get :original-file t)    ; query the local plist
    

    OR

    (org-capture-get :original-file)      ; query the global plist
    

    depending on which plist you want to query. You can then get the directory name by calling file-name-directory on the result.

    One or the other of these should give you the directory which you can then use as a heading. I'm not sure though whether the heading needs to exist already: if it has to exist beforehand, then you must do additional work, work that is not covered by this answer.

    Completely and utterly untested, if that's not clear already.

    EDIT: Here's a slightly more detailed explanation of the global vs local plist.

    When you call org-capture, the various values are saved on the global plist, then eventually a capture buffer is created and the values are copied to the buffer-local plist. At that point, the capture buffer is waiting for input, so you can initiate a different capture: that goes through the same process, so now the global plist has the information for the second capture (until its buffer is created etc).

    So it really depends at what point in the sequence you try to access the value you want: if it happens before the capture buffer is created (as it seems to in your case), then you get it out of the global plist; but if it happens afterwards, you have to get it off the local plist, because the global one may be referring to a different capture.

    So you should probably be doing something like this:

       (let ((path (file-name-directory 
                      (or (org-capture-get :original-file t)
                          (org-capture-get :original-file)))))
          ...
    

    i.e. try to get it off the local plist first and fall back to the global plist only if the local one is nil. That way, you avoid the problem of getting the value from the wrong capture context.

    I hope that clarifies the situation.