3

I'm new to org-mode (and emacs in general), and have been trying to follow the tutorials and manuals to set up a GTD-like system.

I currently have an "inbox.org" file with two first-level headings ("Inbox" and "Processing"). I also have a "todo.org" file with a "Projects" heading.

I've set up org-refile-targets so that any task in inbox.org can be refiled to todo.org. When I refile something, it shows me all the headings in all my org files to choose.

However, what I'd really like to do is have the refile targets change based on the heading or file I'm currently in.

So, for example, for tasks under the "Inbox" heading, the only refile target available should be "Processing".

For tasks under "Processing", the only refile targets available should be "Projects", "Someday" (goes to a someday.org file), or "Reference" (goes to a reference.org file).

This way, I can create sort of a directional "workflow" to enforce the GTD decision model. Is it possible to set this up with org-mode?

1
  • You can control refile target with org-refile-targets. (i.e. C-h v org-refile-targets<ret>)
    – lurdan
    Aug 1, 2016 at 8:50

2 Answers 2

4

org-mode already has a feature like this for org-capture and org-agenda. We can use that to bootstrap a similar feature for org-refile:

(require 'dash)

(defvar org-refile-contexts "Contexts for `org-capture'.

Takes the same values as `org-capture-templates-contexts' except
that the first value of each entry should be a valid setting for
`org-refile-targets'.")

(defun org-refile--get-context-targets ()
  "Get the refile targets for the current headline.

Returns the first set of targets in `org-refile-contexts' that
the current headline satisfies, or `org-refile-targets' if there
are no such."
  (or (car (-first (lambda (x)
                            (org-contextualize-validate-key
                             (car x)
                             org-refile-contexts))
                          org-refile-contexts
                 ))
      org-refile-targets)
  )

(defun org-refile-with-context (&optional arg default-buffer rfloc msg)
  "Refile the headline to a location based on `org-refile-targets'.

Changes the set of available refile targets based on `org-refile-contexts', but is otherwise identical to `org-refile'"
  (interactive "P")
  (let ((org-refile-targets (org-refile--get-context-targets)))
    (org-refile arg default-buffer rfloc msg)
    )
  )

Set the contexts you want in org-refile-contexts using the same rules as org-capture-templates-contexts: it is a list of pairs (targets conditions), where targets is the desired setting for org-refile-targets and conditions is a list of predicates to test the current headline against (most generally, these can be any function taking no arguments).

For example this setting will cause anything in the "notes.org" file under the "Inbox" heading to be refiled to "Projects"

(setq org-refile-contexts
      '((((("notes.org") . (:regexp . "Projects")))
         ((lambda () (string= (org-find-top-headline) "Inbox")))
         )
        ))

More conditions and more targets can be added to achieve almost any workflow.

Bind org-capture-with-context to the key normally bound to org-refile and it should replace it seamlessly:

(define-key org-mode-map (kbd "C-c C-w") #'org-refile-with-context)
1

org-refile uses a rfloc argument that, after reading the docstring, unfortunately remains mysterious. This answer clarifies how rfloc should look like. The following function sets the rfloc based on a FILE argument, that is the target file, a HEADING argument, specifying the target heading the current heading should be refiled to (must be exact, meaning you have to include tags and todo states if there are any), and a PARENT argument that describes the top-level heading whose subitems will be refiled to that target:

(defun org-refile-enh (file heading parent &optional arg default-buffer msg)
  "Refile entries with top-level PARENT under the (exact) HEADING in FILE."
  (let ((rfloc
         (if (equal parent
              (save-excursion
                (re-search-backward "^\* " nil 'noerror 1)
                (nth 4 (org-heading-components))))
             `(,heading
               ,file
               nil
               ,(with-current-buffer (find-file-noselect file)
                  (org-find-exact-headline-in-buffer heading))))))
    (org-refile arg default-buffer rfloc msg)))

In your case, refiling subitems from "Inbox" under the "Processing" heading in the same file would be:

(org-refile-enh "~/path/to/inbox.org" ;target file
                "Processing"          ;target heading
                "Inbox"               ;top-level heading of refiled item
                )

Your Answer

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

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