changeset 101600:10d6e33982c3

(todo-insert-item-here): Prevent insertion of a new entry inside of an existing entry. Minor code cleanup. (todo-add-category): Change the interactive spec. Signal an error if the Todo file is non-empty but contains no category. Reject category names that could induce bugs and confusion. Call todo-mode if the Todo file is new and unsaved. Simplify handling of local variables cookie. Properly display the newly added category in Todo mode. (todo-show): Call todo-initial-setup only if there is neither a Todo file nor a corresponding unsaved buffer. (todo-category-alist): Delete function. (todo-completing-read): New function. (todo-insert-item, todo-jump-to-category): Use it. (todo-insert-item): Make the use of the prefix argument conform to the doc string.
author Chong Yidong <cyd@stupidchicken.com>
date Wed, 28 Jan 2009 03:56:18 +0000
parents d8799ea52d74
children 58bce94ff020
files lisp/calendar/todo-mode.el
diffstat 1 files changed, 73 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/calendar/todo-mode.el	Wed Jan 28 03:56:08 2009 +0000
+++ b/lisp/calendar/todo-mode.el	Wed Jan 28 03:56:18 2009 +0000
@@ -527,28 +527,45 @@
     (narrow-to-region (todo-item-start) (todo-item-end))))
 
 ;;;###autoload
-(defun todo-add-category (cat)
+(defun todo-add-category (&optional cat)
   "Add new category CAT to the TODO list."
-  (interactive "sCategory: ")
-  (save-window-excursion
-    (setq todo-categories (cons cat todo-categories))
-    (find-file todo-file-do)
-    (widen)
-    (goto-char (point-min))
-    (let ((posn (search-forward "-*- mode: todo; " 17 t)))
-      (if posn
-          (progn
-            (goto-char posn)
-            (kill-line))
-        (insert "-*- mode: todo; \n")
-        (forward-char -1)))
-    (insert (format "todo-categories: %S; -*-" todo-categories))
-    (forward-char 1)
-    (insert (format "%s%s%s\n%s\n%s %s\n"
-                    todo-prefix todo-category-beg cat
-                    todo-category-end
-                    todo-prefix todo-category-sep)))
-  0)
+  (interactive)
+  (let ((buf (find-file-noselect todo-file-do t))
+	(prompt "Category: "))
+    (unless (zerop (buffer-size buf))
+      (and (null todo-categories)
+	   (null todo-cats)
+	   (error "Error in %s: File is non-empty but contains no category" 
+		  todo-file-do)))
+    (unless cat (setq cat (read-from-minibuffer prompt)))
+    (with-current-buffer buf
+      ;; reject names that could induce bugs and confusion
+      (while (and (cond ((string= "" cat)
+			 (setq prompt "Enter a non-empty category name: "))
+			((string-match "\\`\\s-+\\'" cat)
+			 (setq prompt "Enter a category name that is not only white space: "))
+			((member cat todo-categories)
+			 (setq prompt "Enter a non-existing category name: ")))
+		  (setq cat (read-from-minibuffer prompt))))
+      ;; initialize a newly created Todo buffer for Todo mode
+      (unless (file-exists-p todo-file-do) (todo-mode))
+      (setq todo-categories (cons cat todo-categories))
+      (widen)
+      (goto-char (point-min))
+      (if (search-forward "-*- mode: todo; " 17 t)
+	  (kill-line)
+	(insert "-*- mode: todo; \n")
+	(forward-char -1))
+      (insert (format "todo-categories: %S; -*-" todo-categories))
+      (forward-char 1)
+      (insert (format "%s%s%s\n%s\n%s %s\n"
+		      todo-prefix todo-category-beg cat
+		      todo-category-end
+		      todo-prefix todo-category-sep))
+      (if (interactive-p)
+	  ;; properly display the newly added category
+	  (progn (setq todo-category-number 0) (todo-show))
+	0))))
 
 ;;;###autoload
 (defun todo-add-item-non-interactively (new-item category)
@@ -596,30 +613,27 @@
 			      "New TODO entry: "
 			      (if todo-entry-prefix-function
 				  (funcall todo-entry-prefix-function)))))
-	   (categories todo-categories)
-	   (history (cons 'categories (1+ todo-category-number)))
 	   (current-category (nth todo-category-number todo-categories))
-	   (category
-	    (if arg
-		current-category
-	      (completing-read (concat "Category [" current-category "]: ")
-			       (todo-category-alist) nil nil nil
-			       history current-category))))
+	   (category (if arg (todo-completing-read) current-category)))
       (todo-add-item-non-interactively new-item category))))
 
 (defalias 'todo-cmd-inst 'todo-insert-item)
 
 (defun todo-insert-item-here ()
-  "Insert new TODO list entry under the cursor."
-  (interactive "")
-  (save-excursion
-    (if (not (derived-mode-p 'todo-mode)) (todo-show))
-    (let* ((new-item (concat todo-prefix " "
-			     (read-from-minibuffer
-			      "New TODO entry: "
-			      (if todo-entry-prefix-function
-				  (funcall todo-entry-prefix-function))))))
-      (insert (concat new-item "\n")))))
+  "Insert a new TODO list entry directly above the entry at point.
+If point is on an empty line, insert the entry there."
+  (interactive)
+  (if (not (derived-mode-p 'todo-mode)) (todo-show))
+  (let ((new-item (concat todo-prefix " "
+			  (read-from-minibuffer
+			   "New TODO entry: "
+			   (if todo-entry-prefix-function
+			       (funcall todo-entry-prefix-function))))))
+    (unless (and (bolp) (eolp)) (goto-char (todo-item-start)))
+    (insert (concat new-item "\n"))
+    (backward-char)
+    ;; put point at start of new entry
+    (goto-char (todo-item-start))))
 
 (defun todo-more-important-p (line)
   "Ask whether entry is more important than the one at LINE."
@@ -801,12 +815,7 @@
 (defun todo-jump-to-category ()
   "Jump to a category.  Default is previous category."
   (interactive)
-  (let* ((categories todo-categories)
-         (history (cons 'categories (1+ todo-category-number)))
-	 (default (nth todo-category-number todo-categories))
-	 (category (completing-read
-                    (concat "Category [" default "]: ")
-                    (todo-category-alist) nil nil nil history default)))
+  (let ((category (todo-completing-read)))
     (if (string= "" category)
         (setq category (nth todo-category-number todo-categories)))
     (setq todo-category-number
@@ -861,9 +870,19 @@
   "Return non-nil if STRING spans several lines."
   (> (todo-string-count-lines string) 1))
 
-(defun todo-category-alist ()
-  "Generate an alist for use in `completing-read' from `todo-categories'."
-  (mapcar #'list todo-categories))
+(defun todo-completing-read ()
+  "Return a category name, with completion, for use in Todo mode."
+  ;; make a copy of todo-categories in case history-delete-duplicates is
+  ;; non-nil, which makes completing-read alter todo-categories
+  (let* ((categories (copy-sequence todo-categories))
+	 (history (cons 'todo-categories (1+ todo-category-number)))
+	 (default (nth todo-category-number todo-categories))
+	 (category (completing-read
+		    (concat "Category [" default "]: ")
+		    todo-categories nil nil nil history default)))
+    ;; restore the original value of todo-categories
+    (setq todo-categories categories)
+    category))
 
 ;; ---------------------------------------------------------------------------
 
@@ -929,7 +948,12 @@
 (defun todo-show ()
   "Show TODO list."
   (interactive)
-  (if (file-exists-p todo-file-do)
+  ;; Call todo-initial-setup only if there is neither a Todo file nor
+  ;; a corresponding unsaved buffer.
+  (if (or (file-exists-p todo-file-do)
+	  (let* ((buf (get-buffer (file-name-nondirectory todo-file-do)))
+		 (bufname (buffer-file-name buf)))
+	    (equal (expand-file-name todo-file-do) bufname)))
       (find-file todo-file-do)
     (todo-initial-setup))
   (if (null todo-categories)