Subject: 25.0.93; Todo mode bug involving archived items

From: Stephen Berman <[email-masked]>
Date: Wed, 04 May 2016 15:53:16 +0200

With todo-mode.el, when a category has archived items and the user
option todo-skip-archived-categories is non-nil, trying to visit the
archived items from the todo file's table of categories buffer results
in an error and a misbehavior.  To reproduce from -Q:

0. Create a todo file and an archive for it, e.g. as follows (this
   assumes there isn't any todo file yet; if there is, then after
   calling todo-show, type `F a' to add a new file, follow the prompts
   to enter an item and proceed with steps b and c):
   a. M-x todo-show RET RET RET RET RET test RET (to create file "Todo"
      with category "Todo" containing item "test").
   b. d (to mark the item as done and move it to the category's done
      items section). 
   c. C-u A d y (to archive the done item and display the archive
      buffer; `y' is typed at the prompt).
1. Kill the Todo Archive mode buffer.
2. M-x customize-option RET todo-skip-archived-categories RET, toggle
   its value to "on", change the state to "SET for current session
   only".
3. Return to the (empty) Todo mode buffer (e.g. by invoking todo-show).
4. Type `F c' to display the todo file's table of categories, put point
   on the line beginning with "1" (e.g. by repeatedly typing TAB or `n')
   and type RET.  This should display the category's archive in Todo
   Archive mode, but instead:
=> The window now displays an empty buffer in Fundamental mode whose
   mode line says "Todo category 1: nil" and the error message "Category
   nil is missing todo-category-done string" is displayed.
5. M-x todo-show should now redisplay the (empty) Todo mode buffer, but
   instead, after invoking that command:
=> The Todo Archive mode buffer is displayed.  Subsequently calling
   todo-show from any non-Todo mode buffer (e.g. *scratch*) wrongly
   displays the Todo Archive mode buffer instead of the Todo mode
   buffer.

These problems do not occur if step 1 is omitted.  (If you already have
a todo file and a corresponding archive file, the recipe can begin from
-Q by invoking todo-show and then continuing with step 2; then there is
no live Todo Archive mode buffer, so the bug gets triggered.)

The patch below fixes these problems (and also removes some code added
in commit d04d6b955b4caaa9817ec053eddb59e923a68cf8 that was made no-op
by 58c7641d1b069be3ead47dbe4a44c8360ef8d1f2 and should have been removed
then).

In GNU Emacs 25.0.93.3 (x86_64-suse-linux-gnu, GTK+ Version 3.14.15)
 of 2016-05-04 built on rosalinde
Repository revision: adc80b7e238e09b1b8c392ecf902d2b978d9016d
Windowing system distributor 'The X.Org Foundation', version 11.0.11601000
System Description:	openSUSE 13.2 (Harlequin) (x86_64)

diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index 0529e97..8e75258 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -902,17 +902,19 @@ todo-jump-to-category
       (todo-show)
     (let* ((archive (eq where 'archive))
 	   (cat (unless archive where))
+           (goto-archive (and cat
+                              todo-skip-archived-categories
+                              (zerop (todo-get-count 'todo cat))
+                              (zerop (todo-get-count 'done cat))
+                              (not (zerop (todo-get-count 'archived cat)))))
 	   (file0 (when cat		; We're in Todo Categories mode.
-		    ;; With non-nil `todo-skip-archived-categories'
-		    ;; jump to archive file of a category with only
-		    ;; archived items.
-		    (if (and todo-skip-archived-categories
-			     (zerop (todo-get-count 'todo cat))
-			     (zerop (todo-get-count 'done cat))
-			     (not (zerop (todo-get-count 'archived cat))))
+		    (if goto-archive
+			;; If the category has only archived items and
+			;; `todo-skip-archived-categories' is non-nil, jump to
+			;; the archive category.
 			(concat (file-name-sans-extension
 				 todo-current-todo-file) ".toda")
-		      ;; Otherwise, jump to current todo file.
+		      ;; Otherwise, jump to the category in the todo file.
 		      todo-current-todo-file)))
 	   (len (length todo-categories))
 	   (cat+file (unless cat
@@ -923,18 +925,15 @@ todo-jump-to-category
 	   (category (or cat (car cat+file))))
       (unless cat (setq file0 (cdr cat+file)))
       (with-current-buffer (find-file-noselect file0 'nowarn)
-	(setq todo-current-todo-file file0)
-	;; If called from Todo Categories mode, clean up before jumping.
-	(if (string= (buffer-name) todo-categories-buffer)
-	    (kill-buffer))
-	(set-window-buffer (selected-window)
-			   (set-buffer (find-buffer-visiting file0)))
-	(unless todo-global-current-todo-file
-	  (setq todo-global-current-todo-file todo-current-todo-file))
-	(todo-category-number category)
-	(todo-category-select)
-	(goto-char (point-min))
-	(when add-item (todo-insert-item--basic))))))
+        (when goto-archive (todo-archive-mode))
+        (set-window-buffer (selected-window)
+                           (set-buffer (find-buffer-visiting file0)))
+        (unless todo-global-current-todo-file
+          (setq todo-global-current-todo-file todo-current-todo-file))
+        (todo-category-number category)
+        (todo-category-select)
+        (goto-char (point-min))
+        (when add-item (todo-insert-item--basic))))))
 
 (defun todo-next-item (&optional count)
   "Move point down to the beginning of the next item.