;;;;;
title: Filtering org tasks
tags: tips, emacs, org-mode
date: 2013-08-25 00:38
format: md
;;;;;

I want to be able to easily see a list of tasks that are relevant to
the currently loaded desktop file. As I use `desktop.el` as a kind of
project system, this means I only want the tasks for the project I'm
currently working on.

First I wrote the `tagify` function, because `org-mode` tags can't
contain `.` or `-` characters (among others I'm sure, but these are
the only ones that have caused me any trouble so far). It's a simple
string replacement:

``` emacs-lisp
(defun tagify (str)
  "Remove dots, replace - with _ in STR."
  (replace-regexp-in-string
   "-" "_" (replace-regexp-in-string "\\." "" (downcase str))))
```

Then I wrote a function to filter the task list by not showing
anything that wasn't tagged with the name of the currently loaded
desktop, unless it isn't tagged at all or no desktop has been loaded.
And set this function to be the value of
`org-agenda-before-sorting-filter-function`.

``` emacs-lisp
(defun filter-by-desktop (entry)
  "Return ENTRY if it has no tags or a tag corresponding to the desktop."
  (require 'desktop)
  (let ((label (when desktop-dirname
                 (tagify (file-name-base
                          (directory-file-name
                           (expand-file-name desktop-dirname))))))
        (tags (get-text-property 0 'tags entry)))
    (when (or (null desktop-dirname) (null tags) (member label tags))
      entry)))

(setq org-agenda-before-sorting-filter-function #'filter-by-desktop)
```

This works fine. I keep untagged tasks in the list as well because
they might be important at all times and I don't want them falling
through the cracks. I also want a complete list if no desktop has been
loaded so I can browse through my tasks and decide what I'm going to
do next.

The downside of this solution is that I have to close my current
project in order to see the whole list.

Then I discovered the `/` key in the agenda buffer. I can't believe I
didn't notice this key before. Anyway, that changed my solution.

Instead of setting `org-agenda-before-sorting-filter-function` I add a
hook to the `org-agenda-finalize-hook`. The `filter-by-desktop`
function looks a little different now:

``` emacs-lisp
(defun org-init-filter-by-desktop ()
  "Filter agenda by current label."
  (when desktop-dirname
    (let ((label (tagify (file-name-base
                          (directory-file-name
                           (expand-file-name desktop-dirname))))))
      (org-agenda-filter-apply (cons label nil) 'tag))))

(add-hook 'org-agenda-finalize-hook 'org-init-filter-by-desktop)
```

I don't need to compare any tags now, and if I want to see my entire
list of tasks I can easily just press `/ /`. Since it is easier to get
back to the overview of my tasks it also doesn't bother me so much
that this way any untagged tasks don't show up in the filtered buffer.

I haven't stopped using `/` since I discovered it. Filtering in this
way is one of the things I like about `ibuffer` as well, now for
`org-mode` it works excellently as well.

<!-- Local Variables: -->
<!-- mode: markdown -->
<!-- End: -->
