emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Eric Abrahamsen <eric@ericabrahamsen.net>
To: emacs-orgmode@gnu.org
Subject: Re: Dimming ancestors in the agenda (relevant to indenting nested TODOs in agenda views)
Date: Sat, 24 Sep 2011 21:51:39 +0800	[thread overview]
Message-ID: <87sjnmoxp0.fsf@ericabrahamsen.net> (raw)
In-Reply-To: <87ipoyyuoq.fsf@ericabrahamsen.net>

Okay, here's an attempt at indicating nested todos in the todo agenda
view.

The more I futzed with comparing consecutive TODOs the hackier it
seemed, so I went with something more fundamental. Right now I think
this is a bigger solution than the problem warranted, but it might also
open the way to other interesting features, so I'm floating it here.

With the attached patch, and the variable
`org-agenda-todo-list-sublevels' set to anything but nil,
`org-agenda-get-todos' will put todos into nested lists, rather than a
flat list.

While that's happening, TODOs can be formatted differently to indicate
their depth within TODO subtrees. Currently that's hardcoded to the
venerable leading dots, but symbol values for
`org-agenda-todo-list-sublevels' could provide for a wider variety of
formatting options, or a custom function.

Then `org-finalize-agenda-entries' unwinds the nested lists back into a
flat list (using a "flatten" pattern straight out of Paul Graham),
applying the necessary filtering, highlighting and sorting functions
along the way. One bonus is that each level of TODO subtrees gets sorted
distinctly.

Two questions:

1. Is this buggy or broken or unnecessarily slow?
2. Is this justified? Can we do other cool stuff with this?

That's all. Hope it works!

Eric

diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index b1fa5f5..417566d 100644
--- a/lisp/org-agenda.el
+++ b/lisp/org-agenda.el
@@ -587,12 +587,19 @@ When nil, these trees are also scanned by agenda commands."
   :type 'boolean)
 
 (defcustom org-agenda-todo-list-sublevels t
-  "Non-nil means check also the sublevels of a TODO entry for TODO entries.
-When nil, the sublevels of a TODO entry are not checked, resulting in
-potentially much shorter TODO lists."
+  "How to display TODO entries that are sublevels of a TODO entry.
+When nil, the sublevels of a TODO entry are not returned,
+resulting in potentially much shorter TODO lists. When t, the
+default, show all TODO entries as a flat list. 'indent and 'fade
+change the way TODO entries that follow a parent TODO are
+displayed."
   :group 'org-agenda-skip
   :group 'org-agenda-todo-list
-  :type 'boolean)
+  :type '(choice
+	  (const :tag "Create a flat list of sublevels" t)
+	  (const :tag "Do not list sublevel todos" nil)
+	  (const :tag "Indent sublevel todos" indent)
+	  (const :tag "Fade out sublevel todos" fade)))
 
 (defcustom org-agenda-todo-ignore-with-date nil
   "Non-nil means don't show entries with a date in the global todo list.
@@ -4582,40 +4590,57 @@ the documentation of `org-diary'."
 				       (mapconcat 'identity
 						  (org-split-string
 						   org-select-this-todo-keyword "|") "\\|")
-				     "\\)\\>"))
+				       "\\)\\>"))
 			   org-not-done-regexp)
 			 "[^\n\r]*\\)"))
-	 marker priority category category-pos tags todo-state ee txt beg end)
-    (goto-char (point-min))
-    (while (re-search-forward regexp nil t)
-      (catch :skip
-	(save-match-data
-	  (beginning-of-line)
-	  (org-agenda-skip)
-	  (setq beg (point) end (save-excursion (outline-next-heading) (point)))
-	  (when (org-agenda-check-for-timestamp-as-reason-to-ignore-todo-item end)
-	    (goto-char (1+ beg))
-	    (or org-agenda-todo-list-sublevels (org-end-of-subtree 'invisible))
-	    (throw :skip nil)))
-	(goto-char (match-beginning 1))
-	(setq marker (org-agenda-new-marker (match-beginning 0))
-	      category (org-get-category)
-	      category-pos (get-text-property (point) 'org-category-position)
-	      txt (match-string 1)
-	      tags (org-get-tags-at (point))
-	      txt (org-format-agenda-item "" txt category tags)
-	      priority (1+ (org-get-priority txt))
-	      todo-state (org-get-todo-state))
-	(org-add-props txt props
-	  'org-marker marker 'org-hd-marker marker
-	  'priority priority 'org-category category
-	  'org-category-position category-pos
-	  'type "todo" 'todo-state todo-state)
-	(push txt ee)
-	(if org-agenda-todo-list-sublevels
-	    (goto-char (match-end 1))
-	  (org-end-of-subtree 'invisible))))
-    (nreverse ee)))
+	 (depth 0))
+
+    (flet ((mk-td (start finish)
+		  (goto-char start)
+		  (let (marker priority category category-pos tags todo-state ee txt beg end)
+		    (while (re-search-forward regexp finish t)
+		      (catch :skip
+			(save-match-data
+			  (beginning-of-line)
+			  (org-agenda-skip)
+			  (setq beg (point) end (save-excursion (outline-next-heading) (point)))
+			  (when (org-agenda-check-for-timestamp-as-reason-to-ignore-todo-item end)
+			    (goto-char (1+ beg))
+			    (or org-agenda-todo-list-sublevels (org-end-of-subtree 'invisible))
+			    (throw :skip nil)))
+			(goto-char (match-beginning 1))
+			(setq marker (org-agenda-new-marker (match-beginning 0))
+			      category (org-get-category)
+			      category-pos (get-text-property (point) 'org-category-position)
+			      txt (concat (if
+					      (> depth 0)
+					      (make-string (* 2 depth) ?.)
+					    "") (match-string 1))
+			      tags (org-get-tags-at (point))
+			      txt (org-format-agenda-item "" txt category tags depth)
+			      priority (1+ (org-get-priority txt))
+			      todo-state (org-get-todo-state))
+			(org-add-props txt props
+			  'org-marker marker 'org-hd-marker marker
+			  'priority priority 'org-category category
+			  'org-category-position category-pos
+			  'type "todo" 'todo-state todo-state)
+			(if (not org-agenda-todo-list-sublevels)
+			    (progn
+			      (push txt ee)
+			      (org-end-of-subtree 'invisible))
+			  (goto-char (match-end 0))
+			  (let* ((depth (1+ depth))
+				 (subtree-todos
+				  (mk-td (point)
+					 (save-excursion (org-end-of-subtree 'invisible) (point)))))
+			    (when subtree-todos
+			      (setq txt (cons txt (nreverse subtree-todos))))
+			    (push txt ee)))))
+		    ee)))
+      (goto-char (point-min))
+      (nreverse (mk-td (point) (point-max))))))
+
 
 (defun org-agenda-todo-custom-ignore-p (time n)
   "Check whether timestamp is farther away then n number of days.
@@ -5785,12 +5810,18 @@ could bind the variable in the options section of a custom command.")
 
 (defun org-finalize-agenda-entries (list &optional nosort)
   "Sort and concatenate the agenda items."
-  (setq list (mapcar 'org-agenda-highlight-todo list))
-  (if nosort
-      list
-    (when org-agenda-before-sorting-filter-function
-      (setq list (delq nil (mapcar org-agenda-before-sorting-filter-function list))))
-    (mapconcat 'identity (sort list 'org-entries-lessp) "\n")))
+  (flet ((flatten (l)
+		 (cond
+		  ((null l) nil)
+		  ((atom l) (if (and org-agenda-before-sorting-filter-function
+				     (not (org-agenda-before-sorting-filter-function l)))
+				nil
+			      (list (org-agenda-highlight-todo l))))
+		  (t (append (flatten (car l))
+			     (flatten (if nosort
+					  (cdr l)
+					(sort (cdr l) 'org-entries-lessp))))))))
+    (mapconcat 'identity (flatten list) "\n")))
 
 (defun org-agenda-highlight-todo (x)
(let ((org-done-keywords org-done-keywords-for-agenda)
@@ -5926,6 +5957,10 @@ could bind the variable in the options section of a custom command.")
 
 (defun org-entries-lessp (a b)
   "Predicate for sorting agenda entries."
+  (when (consp a)
+    (setq a (car a)))
+  (when (consp b)
+    (setq b (car b)))
   ;; The following variables will be used when the form is evaluated.
   ;; So even though the compiler complains, keep them.
   (let* ((ss org-agenda-sorting-strategy-selected)

  reply	other threads:[~2011-09-24 13:52 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-08-23 18:40 Samuel Wales
2011-08-24  7:07 ` Eric Abrahamsen
2011-09-10 21:02   ` Samuel Wales
2011-09-12  9:30     ` Eric Abrahamsen
2011-09-24 13:51       ` Eric Abrahamsen [this message]
2011-09-24 13:55         ` Eric Abrahamsen
2011-09-24 23:54         ` Samuel Wales
2011-09-25  3:59           ` Eric Abrahamsen
2011-09-25  4:55             ` Samuel Wales
2011-09-25  5:52               ` Eric Abrahamsen
2012-04-23 23:10         ` Bastien
2012-04-25  6:25           ` Eric Abrahamsen
2012-04-26 13:44             ` Bastien

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87sjnmoxp0.fsf@ericabrahamsen.net \
    --to=eric@ericabrahamsen.net \
    --cc=emacs-orgmode@gnu.org \
    --subject='Re: Dimming ancestors in the agenda (relevant to indenting nested TODOs in agenda views)' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Code repositories for project(s) associated with this inbox:

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).