From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Tom Breton (Tehom)" Subject: Feature request and patch - blocked TODO to say BLOCKED Date: Thu, 1 Jan 2009 17:53:57 -0500 (EST) Message-ID: <1131.66.30.178.137.1230850437.squirrel@mail.panix.com> References: <20090101170227.C707734803@mail2.panix.com> Mime-Version: 1.0 Content-Type: text/plain;charset=iso-8859-1 Content-Transfer-Encoding: quoted-printable Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LIWQR-0001J2-0G for emacs-orgmode@gnu.org; Thu, 01 Jan 2009 17:53:59 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LIWQQ-0001Iq-7P for emacs-orgmode@gnu.org; Thu, 01 Jan 2009 17:53:58 -0500 Received: from [199.232.76.173] (port=40342 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LIWQQ-0001In-4Y for emacs-orgmode@gnu.org; Thu, 01 Jan 2009 17:53:58 -0500 Received: from mail2.panix.com ([166.84.1.73]:58444) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LIWQP-0002ze-OU for emacs-orgmode@gnu.org; Thu, 01 Jan 2009 17:53:57 -0500 Received: from mailbackend.panix.com (mailbackend.panix.com [166.84.1.89]) by mail2.panix.com (Postfix) with ESMTP id 61B6B34808 for ; Thu, 1 Jan 2009 17:53:57 -0500 (EST) Received: from mail.panix.com (localhost [127.0.0.1]) by mailbackend.panix.com (Postfix) with ESMTP id 49FA210231 for ; Thu, 1 Jan 2009 17:53:57 -0500 (EST) In-Reply-To: <20090101170227.C707734803@mail2.panix.com> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org Motivating incident: I had a todo-type item that contained no tasks itself, directly, but linked to other tasks. I arranged it this way in order that those other tasks could be placed neatly in a hierarchy and not appear as "TODO" in two places. I used the org-depend.el BLOCKER code to manage this situation. BTW, it works nicely. But when a task is blocked, the heading is left with no "TODO" marking at all. That's not so bad for sibling tasks, because there's one right above it that says "TODO" (or something). But for distant-link style tasks, IMO it gives a misleading impression that there is nothing to do. I request the following: * Object: One TODO workflow keyword set that relates specially to "BLOCKER". It would be something like (sequence "BLOCKED" "|" "UNBLOCKED"). * Behavior: When a C-c C-t change to a heading is blocked, instead of doing nothing, mark the heading with the first entry in the blockage workflow keyword set. * Behavior: Also do so when there is a blocked C-c C-t change to any heading whose TODO mark is in a state in the blockage workflow keyword set. * Behavior: Ordinarily don't offer the blockage workflow keyword set for C-c C-t and related commands. I append a patch which does this. The change mostly affects org.el, because allowing new TODO keywords requires org.el to know about them, at least in computing some regular expressions. It was also a lot neater to let org-todo react to blocked transitions than to try to make `org-depend-block-todo' do it, which also avoided `org-todo' indirectly calling itself. Summary of changes Added 3 variables: * org-todo-use-blockage-keywords-p :: whether to use this * org-todo-blockage-keywords :: configurable keywords * org-blockage-keywords-1 :: internal Encapsulated part of `org-set-regexps-and-options' as `org-set-todo-constants'. I had to because it needed to be called twice, once for normal keywords and once with a flag for blockage keywords. I used encap-sexp to do it automatically (it's on my site, http://panix.com/~tehom/my-code/), so no code changed, and I kept that comment aligned with the regexp. Made `org-set-regexps-and-options' also process `org-todo-blockage-keywords'. Changed the behavior of org-todo and org-depend-block-todo as described above. Tom Breton (Tehom) *** org-depend.el 2008-12-18 18:26:05.000000000 -0500 --- new-org-depend.el 2009-01-01 17:13:13.000000000 -0500 *************** *** 196,204 **** (unless (eq type 'todo-state-change) ;; We are not handling this kind of change (throw 'return t)) ! (unless (and (not from) (member to org-not-done-keywords)) ! ;; This is not a change from nothing to TODO, ignore it ! (throw 'return t)) ;; OK, the plan is to switch from nothing to TODO ;; Lets see if we will allow it. Find the BLOCKER property --- 196,210 ---- (unless (eq type 'todo-state-change) ;; We are not handling this kind of change (throw 'return t)) ! ;;Act on only the right types of TODO-change: ! (unless ! (or ! ;;A change from a member of the blockage set ! (member from org-todo-blockage-keywords) ! ;;A change from nothing to TODO ! (and (not from) (member to org-not-done-keywords))) ! ;; Otherwise ignore it ! (throw 'return t)) ;; OK, the plan is to switch from nothing to TODO ;; Lets see if we will allow it. Find the BLOCKER property *** old-org.el 2008-12-18 18:26:05.000000000 -0500 --- org.el 2009-01-01 17:25:32.000000000 -0500 *************** *** 1458,1464 **** (const :tag "Type (cycling directly to DONE)" type)) (repeat (string :tag "Keyword")))))) - (defvar org-todo-keywords-1 nil "All TODO and DONE keywords active in a buffer.") (make-variable-buffer-local 'org-todo-keywords-1) --- 1458,1463 ---- *************** *** 1483,1488 **** --- 1482,1494 ---- (make-variable-buffer-local 'org-todo-key-alist) (defvar org-todo-key-trigger nil) (make-variable-buffer-local 'org-todo-key-trigger) + (defvar org-todo-use-blockage-keywords-p t) + (defvar org-todo-blockage-keywords + '(sequence "BLOCKED" "|" "UNBLOCKED") + "Keywords of the blocking workflow keyword set." ) + (defvar org-blockage-keywords-1 nil + "All blockage keywords active in a buffer" ) + (make-variable-buffer-local 'org-blockage-keywords-1) (defcustom org-todo-interpretation 'sequence "Controls how TODO keywords are interpreted. *************** *** 2997,3002 **** --- 3003,3079 ---- set this variable to if the option is found. An optional forth element PUSH means to push this value onto the list in the variable.") + (defun org-set-todo-constants (kwds block) + "" + (let + (inter kws kw) + (while + (setq kws (pop kwds)) + (setq + inter (pop kws) + sep (member "|" kws) + kws0 (delete "|" + (copy-sequence kws)) + kwsa nil + kws1 + (mapcar + (lambda + (x) + (if + ;; 1 2 + (string-match "^\\(.*?\\)\\(?:(\\([^!@/]\\)?.*?)\\)?$" x) + (progn + (setq kw + (match-string 1 x) + key + (and + (match-end 2) + (match-string 2 x)) + log + (org-extract-log-state-settings x)) + (push + (cons kw + (and key + (string-to-char key))) + kwsa) + (and log + (push log org-todo-log-states)) + kw) + (error "Invalid TODO keyword %s" x))) + kws0) + kwsa + (if kwsa + (append + '((:startgroup)) + (nreverse kwsa) + '((:endgroup)))) + hw (car kws1) + dws (if sep + (org-remove-keyword-keys + (cdr sep)) + (last kws1)) + tail (list inter hw + (car dws) + (org-last dws))) + (add-to-list 'org-todo-heads hw 'append) + (push kws1 org-todo-sets) + (setq org-done-keywords + (append org-done-keywords dws nil)) + (setq org-todo-key-alist + (append org-todo-key-alist kwsa)) + (mapc + (lambda + (x) + (push + (cons x tail) + org-todo-kwd-alist)) + kws1) + (if block + (setq org-blockage-keywords-1 + (append org-blockage-keywords-1 kws1 nil))) + (setq org-todo-keywords-1 + (append org-todo-keywords-1 kws1 nil))))) + (defun org-set-regexps-and-options () "Precompute regular expressions for current buffer." (when (org-mode-p) *************** *** 3116,3156 **** (setq kwds (list (cons org-todo-interpretation (default-value 'org-todo-keywords))))) (setq kwds (reverse kwds))) ! (setq kwds (nreverse kwds)) ! (let (inter kws kw) ! (while (setq kws (pop kwds)) ! (setq inter (pop kws) sep (member "|" kws) ! kws0 (delete "|" (copy-sequence kws)) ! kwsa nil ! kws1 (mapcar ! (lambda (x) ! ;; 1 2 ! (if (string-match "^\\(.*?\\)\\(?:(\\([^!@/]\\)?.*?)\\)?$" x) ! (progn ! (setq kw (match-string 1 x) ! key (and (match-end 2) (match-string 2 x)) ! log (org-extract-log-state-settings x)) ! (push (cons kw (and key (string-to-char key))) kwsa) ! (and log (push log org-todo-log-states)) ! kw) ! (error "Invalid TODO keyword %s" x))) ! kws0) ! kwsa (if kwsa (append '((:startgroup)) ! (nreverse kwsa) ! '((:endgroup)))) ! hw (car kws1) ! dws (if sep (org-remove-keyword-keys (cdr sep)) (last kws1)) ! tail (list inter hw (car dws) (org-last dws))) ! (add-to-list 'org-todo-heads hw 'append) ! (push kws1 org-todo-sets) ! (setq org-done-keywords (append org-done-keywords dws nil)) ! (setq org-todo-key-alist (append org-todo-key-alist kwsa)) ! (mapc (lambda (x) (push (cons x tail) org-todo-kwd-alist)) kws1) ! (setq org-todo-keywords-1 (append org-todo-keywords-1 kws1 nil))) ! (setq org-todo-sets (nreverse org-todo-sets) ! org-todo-kwd-alist (nreverse org-todo-kwd-alist) ! org-todo-key-trigger (delq nil (mapcar 'cdr org-todo-key-alist)) ! org-todo-key-alist (org-assign-fast-keys org-todo-key-alist))) ;; Process the constants (when const (let (e cst) --- 3193,3209 ---- (setq kwds (list (cons org-todo-interpretation (default-value 'org-todo-keywords))))) (setq kwds (reverse kwds))) ! ;;(setq kwds (append (nreverse kwds) (list org-todo-blockage-keywords))) ! (org-set-todo-constants (nreverse kwds) nil) ! (when org-todo-use-blockage-keywords-p ! (org-set-todo-constants (list org-todo-blockage-keywords) t)) ! (setq ! org-todo-sets (nreverse org-todo-sets) ! org-todo-kwd-alist (nreverse org-todo-kwd-alist) ! org-todo-key-trigger (delq nil ! (mapcar 'cdr org-todo-key-alist)) ! org-todo-key-alist (org-assign-fast-keys org-todo-key-alist)) ! ;; Process the constants (when const (let (e cst) *************** *** 8222,8232 **** (save-match-data (run-hook-with-args-until-failure 'org-blocker-hook change-plist))) ! (if (interactive-p) ! (error "TODO state change from %s to %s blocked" this state) ! ;; fail silently ! (message "TODO state change from %s to %s blocked" this state) ! (throw 'exit nil)))) (store-match-data match-data) (replace-match next t t) (unless (pos-visible-in-window-p hl-pos) --- 8275,8294 ---- (save-match-data (run-hook-with-args-until-failure 'org-blocker-hook change-plist))) ! (if ! org-todo-use-blockage-keywords-p ! (let ! ((blocked-state (car org-blockage-keywords-1))) ! (setq arg 'none) ! (setq next (concat " " blocked-state " ")) ! (unless ! (string=3D state blocked-state) ! (message "TODO state change from %s to %s blocked" this state))= ) ! (if (interactive-p) ! (error "TODO state change from %s to %s blocked" this state) ! ;; fail silently ! (message "TODO state change from %s to %s blocked" this state) ! (throw 'exit nil))))) (store-match-data match-data) (replace-match next t t) (unless (pos-visible-in-window-p hl-pos)