aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Berman2013-07-05 16:03:35 +0200
committerStephen Berman2013-07-05 16:03:35 +0200
commitd610f6dd0f7afd919c243e1c0786174010c19be8 (patch)
tree49c6a022508410b20c1a7d75ab198f973bc9e0f4
parentddbdfd6f90c513e15ad5896dea156110f048322f (diff)
downloademacs-d610f6dd0f7afd919c243e1c0786174010c19be8.tar.gz
emacs-d610f6dd0f7afd919c243e1c0786174010c19be8.zip
* calendar/todo-mode.el: Add handling of file deletion, both by
mode command and externally. Fix various related bugs. Clarify Commentary and improve some documentation strings and code. (todo-delete-file): New command. (todo-check-file): New function. (todo-show): Handle external deletion of the file we're trying to show. Replace called-interactively-p by an optional prefix argument to avoid problematic interaction with catch form when byte compiled (bug#14702). (todo-quit): Handle external deletion of the archive's todo file. Make sure the buffer that was visiting the archive file is still live before trying to bury it. (todo-category-completions): Handle external deletion of any category completion files. (todo-jump-to-category, todo-basic-insert-item): Recalculate list of todo files, in case of external deletion. (todo-add-file): Replace unnecessary setq by let-binding. (todo-find-archive): Check whether there are any archives. Replace unnecessary setq by let-binding. (todo-archive-done-item): Use find-file-noselect to get the archive buffer whether or not the archive already exists. Remove superfluous code. Use file size instead of buffer-file-name to check if the archive is new; if it is, update list of archives. (todo-default-todo-file): Allow nil to be a valid value for when there are no todo files. (todo-reevaluate-default-file-defcustom): Use corrected definition of todo-default-todo-file. (todo-key-bindings-t+a+f): Add key binding for todo-delete-file. (todo-delete-category, todo-show-categories-table) (todo-category-number): Clarify comment. (todo-filter-items): Clarify documentation string. (todo-show-current-file, todo-display-as-todo-file) (todo-reset-and-enable-done-separator): Tweak documentation string. (todo-done-separator): Make separator length window-width, since bug#2749 is now fixed. Fixes: debbugs:14688
-rw-r--r--lisp/ChangeLog38
-rw-r--r--lisp/calendar/todo-mode.el539
2 files changed, 379 insertions, 198 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 3dfa2f4a11b..46c728abbe7 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,41 @@
12013-07-05 Stephen Berman <stephen.berman@gmx.net>
2
3 * calendar/todo-mode.el: Add handling of file deletion, both by
4 mode command and externally. Fix various related bugs. Clarify
5 Commentary and improve some documentation strings and code.
6 (todo-delete-file): New command.
7 (todo-check-file): New function.
8 (todo-show): Handle external deletion of the file we're trying to
9 show (bug#14688). Replace called-interactively-p by an optional
10 prefix argument to avoid problematic interaction with catch form
11 when byte compiled (bug#14702).
12 (todo-quit): Handle external deletion of the archive's todo file.
13 Make sure the buffer that was visiting the archive file is still
14 live before trying to bury it.
15 (todo-category-completions): Handle external deletion of any
16 category completion files.
17 (todo-jump-to-category, todo-basic-insert-item): Recalculate list
18 of todo files, in case of external deletion.
19 (todo-add-file): Replace unnecessary setq by let-binding.
20 (todo-find-archive): Check whether there are any archives.
21 Replace unnecessary setq by let-binding.
22 (todo-archive-done-item): Use find-file-noselect to get the
23 archive buffer whether or not the archive already exists. Remove
24 superfluous code. Use file size instead of buffer-file-name to
25 check if the archive is new; if it is, update list of archives.
26 (todo-default-todo-file): Allow nil to be a valid value for when
27 there are no todo files.
28 (todo-reevaluate-default-file-defcustom): Use corrected definition
29 of todo-default-todo-file.
30 (todo-key-bindings-t+a+f): Add key binding for todo-delete-file.
31 (todo-delete-category, todo-show-categories-table)
32 (todo-category-number): Clarify comment.
33 (todo-filter-items): Clarify documentation string.
34 (todo-show-current-file, todo-display-as-todo-file)
35 (todo-reset-and-enable-done-separator): Tweak documentation string.
36 (todo-done-separator): Make separator length window-width, since
37 bug#2749 is now fixed.
38
12013-07-05 Michael Albinus <michael.albinus@gmx.de> 392013-07-05 Michael Albinus <michael.albinus@gmx.de>
2 40
3 * net/tramp-sh.el (tramp-sh-handle-file-notify-add-watch): Support 41 * net/tramp-sh.el (tramp-sh-handle-file-notify-add-watch): Support
diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index a497f759e87..934dfb92a57 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -37,11 +37,14 @@
37;; can edit todo items, reprioritize them within their category, move 37;; can edit todo items, reprioritize them within their category, move
38;; them to another category, delete them, or mark items as done and 38;; them to another category, delete them, or mark items as done and
39;; store them separately from the not yet done items in a category. 39;; store them separately from the not yet done items in a category.
40;; You can add new todo files and categories, rename categories, move 40;; You can add new todo files, edit and delete them. You can add new
41;; them to another file or delete them. You can also display summary 41;; categories, rename and delete them, move categories to another file
42;; tables of the categories in a file and the types of items they 42;; and merge the items of two categories. You can also reorder the
43;; contain. And you can build cross-category lists of items that 43;; sequence of categories in a todo file for the purpose of
44;; satisfy various criteria. 44;; navigation. You can display summary tables of the categories in a
45;; file and the types of items they contain. And you can compile
46;; lists of existing items from multiple categories in one or more
47;; todo files, which are filtered by various criteria.
45 48
46;; To get started, load this package and type `M-x todo-show'. This 49;; To get started, load this package and type `M-x todo-show'. This
47;; will prompt you for the name of the first todo file, its first 50;; will prompt you for the name of the first todo file, its first
@@ -169,12 +172,7 @@ the value of `todo-done-separator'."
169 "Return string used as value of variable `todo-done-separator'." 172 "Return string used as value of variable `todo-done-separator'."
170 (let ((sep todo-done-separator-string)) 173 (let ((sep todo-done-separator-string))
171 (propertize (if (= 1 (length sep)) 174 (propertize (if (= 1 (length sep))
172 ;; Until bug#2749 is fixed, if separator's length 175 (make-string (window-width) (string-to-char sep))
173 ;; is window-width and todo-wrap-lines is
174 ;; non-nil, an indented empty line appears between
175 ;; the separator and the first done item.
176 ;; (make-string (window-width) (string-to-char sep))
177 (make-string (1- (window-width)) (string-to-char sep))
178 todo-done-separator-string) 176 todo-done-separator-string)
179 'face 'todo-done-sep))) 177 'face 'todo-done-sep)))
180 178
@@ -578,11 +576,12 @@ This lacks the extension and directory components."
578 (file-name-sans-extension (file-name-nondirectory file)))) 576 (file-name-sans-extension (file-name-nondirectory file))))
579 577
580(defcustom todo-default-todo-file (todo-short-file-name 578(defcustom todo-default-todo-file (todo-short-file-name
581 (car (funcall todo-files-function))) 579 (car (funcall todo-files-function)))
582 "Todo file visited by first session invocation of `todo-show'." 580 "Todo file visited by first session invocation of `todo-show'."
583 :type `(radio ,@(mapcar (lambda (f) (list 'const f)) 581 :type (when todo-files
584 (mapcar 'todo-short-file-name 582 `(radio ,@(mapcar (lambda (f) (list 'const f))
585 (funcall todo-files-function)))) 583 (mapcar 'todo-short-file-name
584 (funcall todo-files-function)))))
586 :group 'todo) 585 :group 'todo)
587 586
588(defcustom todo-show-current-file t 587(defcustom todo-show-current-file t
@@ -630,7 +629,7 @@ Otherwise, `todo-show' always visits `todo-default-todo-file'."
630 :group 'todo) 629 :group 'todo)
631 630
632;;;###autoload 631;;;###autoload
633(defun todo-show (&optional solicit-file) 632(defun todo-show (&optional solicit-file interactive)
634 "Visit a todo file and display one of its categories. 633 "Visit a todo file and display one of its categories.
635 634
636When invoked in Todo mode, prompt for which todo file to visit. 635When invoked in Todo mode, prompt for which todo file to visit.
@@ -668,117 +667,124 @@ and done items are always shown on visiting a category.
668 667
669Invoking this command in Todo Archive mode visits the 668Invoking this command in Todo Archive mode visits the
670corresponding todo file, displaying the corresponding category." 669corresponding todo file, displaying the corresponding category."
671 (interactive "P") 670 (interactive "P\np")
671 (when todo-default-todo-file
672 (todo-check-file (todo-absolute-file-name todo-default-todo-file)))
672 (catch 'shown 673 (catch 'shown
673 ;; If there is a legacy todo file but no todo file in the current 674 ;; Before initializing the first todo first, check if there is a
674 ;; format, offer to convert the legacy file and show it. 675 ;; legacy todo file and if so, offer to convert to the current
676 ;; format and make it the first new todo file.
675 (unless todo-default-todo-file 677 (unless todo-default-todo-file
676 (let ((legacy-todo-file (if (boundp 'todo-file-do) 678 (let ((legacy-todo-file (if (boundp 'todo-file-do)
677 todo-file-do 679 todo-file-do
678 (locate-user-emacs-file "todo-do" ".todo-do")))) 680 (locate-user-emacs-file "todo-do" ".todo-do"))))
679 (when (and (file-exists-p legacy-todo-file) 681 (when (and (file-exists-p legacy-todo-file)
680 (y-or-n-p (concat "Do you want to convert a copy of your " 682 (y-or-n-p (concat "Do you want to convert a copy of your "
681 "old todo file to the new format? "))) 683 "old todo file to the new format? ")))
682 (when (todo-convert-legacy-files) 684 (when (todo-convert-legacy-files)
683 (throw 'shown nil))))) 685 (throw 'shown nil)))))
684 (let* ((cat) 686 (catch 'end
685 (show-first todo-show-first) 687 (let* ((cat)
686 (file (cond ((or solicit-file 688 (show-first todo-show-first)
687 (and (called-interactively-p 'any) 689 (file (cond ((or solicit-file
688 (memq major-mode '(todo-mode 690 (and interactive
689 todo-archive-mode 691 (memq major-mode '(todo-mode
690 todo-filtered-items-mode)))) 692 todo-archive-mode
691 (if (funcall todo-files-function) 693 todo-filtered-items-mode))))
692 (todo-read-file-name "Choose a todo file to visit: " 694 (if (funcall todo-files-function)
693 nil t) 695 (todo-read-file-name "Choose a todo file to visit: "
694 (user-error "There are no todo files"))) 696 nil t)
695 ((and (eq major-mode 'todo-archive-mode) 697 (user-error "There are no todo files")))
696 ;; Called noninteractively via todo-quit 698 ((and (eq major-mode 'todo-archive-mode)
697 ;; to jump to corresponding category in 699 ;; Called noninteractively via todo-quit
698 ;; todo file. 700 ;; to jump to corresponding category in
699 (not (called-interactively-p 'any))) 701 ;; todo file.
700 (setq cat (todo-current-category)) 702 (not interactive))
701 (concat (file-name-sans-extension 703 (setq cat (todo-current-category))
702 todo-current-todo-file) ".todo")) 704 (concat (file-name-sans-extension
703 (t 705 todo-current-todo-file) ".todo"))
704 (or todo-current-todo-file 706 (t
705 (and todo-show-current-file 707 (or todo-current-todo-file
706 todo-global-current-todo-file) 708 (and todo-show-current-file
707 (todo-absolute-file-name todo-default-todo-file) 709 todo-global-current-todo-file)
708 (todo-add-file))))) 710 (todo-absolute-file-name todo-default-todo-file)
709 add-item first-file) 711 (todo-add-file)))))
710 (unless todo-default-todo-file 712 add-item first-file)
711 ;; We just initialized the first todo file, so make it the default. 713 (unless todo-default-todo-file
712 (setq todo-default-todo-file (todo-short-file-name file) 714 ;; We just initialized the first todo file, so make it the default.
713 first-file t) 715 (setq todo-default-todo-file (todo-short-file-name file)
714 (todo-reevaluate-default-file-defcustom)) 716 first-file t)
715 (unless (member file todo-visited) 717 (todo-reevaluate-default-file-defcustom))
716 ;; Can't setq t-c-t-f here, otherwise wrong file shown when 718 (unless (member file todo-visited)
717 ;; todo-show is called from todo-show-categories-table. 719 ;; Can't setq t-c-t-f here, otherwise wrong file shown when
718 (let ((todo-current-todo-file file)) 720 ;; todo-show is called from todo-show-categories-table.
719 (cond ((eq todo-show-first 'table) 721 (let ((todo-current-todo-file file))
720 (todo-show-categories-table)) 722 (cond ((eq todo-show-first 'table)
721 ((memq todo-show-first '(top diary regexp)) 723 (todo-show-categories-table))
722 (let* ((shortf (todo-short-file-name file)) 724 ((memq todo-show-first '(top diary regexp))
723 (fi-file (todo-absolute-file-name 725 (let* ((shortf (todo-short-file-name file))
724 shortf todo-show-first))) 726 (fi-file (todo-absolute-file-name
725 (when (eq todo-show-first 'regexp) 727 shortf todo-show-first)))
726 (let ((rxfiles (directory-files todo-directory t 728 (when (eq todo-show-first 'regexp)
727 ".*\\.todr$" t))) 729 (let ((rxfiles (directory-files todo-directory t
728 (when (and rxfiles (> (length rxfiles) 1)) 730 ".*\\.todr$" t)))
729 (let ((rxf (mapcar 'todo-short-file-name rxfiles))) 731 (when (and rxfiles (> (length rxfiles) 1))
730 (setq fi-file (todo-absolute-file-name 732 (let ((rxf (mapcar 'todo-short-file-name rxfiles)))
731 (completing-read 733 (setq fi-file (todo-absolute-file-name
732 "Choose a regexp items file: " 734 (completing-read
733 rxf) 'regexp)))))) 735 "Choose a regexp items file: "
734 (if (file-exists-p fi-file) 736 rxf) 'regexp))))))
735 (set-window-buffer 737 (if (file-exists-p fi-file)
736 (selected-window) 738 (set-window-buffer
737 (set-buffer (find-file-noselect fi-file 'nowarn))) 739 (selected-window)
738 (message "There is no %s file for %s" 740 (set-buffer (find-file-noselect fi-file 'nowarn)))
739 (cond ((eq todo-show-first 'top) 741 (message "There is no %s file for %s"
740 "top priorities") 742 (cond ((eq todo-show-first 'top)
741 ((eq todo-show-first 'diary) 743 "top priorities")
742 "diary items") 744 ((eq todo-show-first 'diary)
743 ((eq todo-show-first 'regexp) 745 "diary items")
744 "regexp items")) 746 ((eq todo-show-first 'regexp)
745 shortf) 747 "regexp items"))
746 (setq todo-show-first 'first))))))) 748 shortf)
747 (when (or (member file todo-visited) 749 (setq todo-show-first 'first)))))))
748 (eq todo-show-first 'first)) 750 (when (or (member file todo-visited)
749 (set-window-buffer (selected-window) 751 (eq todo-show-first 'first))
750 (set-buffer (find-file-noselect file 'nowarn))) 752 (unless (todo-check-file file) (throw 'end nil))
751 ;; When quitting an archive file, show the corresponding 753 (set-window-buffer (selected-window)
752 ;; category in the corresponding todo file, if it exists. 754 (set-buffer (find-file-noselect file 'nowarn)))
753 (when (assoc cat todo-categories) 755 ;; When quitting an archive file, show the corresponding
754 (setq todo-category-number (todo-category-number cat))) 756 ;; category in the corresponding todo file, if it exists.
755 ;; If this is a new todo file, add its first category. 757 (when (assoc cat todo-categories)
756 (when (zerop (buffer-size)) 758 (setq todo-category-number (todo-category-number cat)))
757 (let (cat-added) 759 ;; If this is a new todo file, add its first category.
758 (unwind-protect 760 (when (zerop (buffer-size))
759 (setq todo-category-number 761 (let (cat-added)
760 (todo-add-category todo-current-todo-file "") 762 (unwind-protect
761 add-item todo-add-item-if-new-category 763 (setq todo-category-number
762 cat-added t) 764 (todo-add-category todo-current-todo-file "")
763 (if cat-added 765 add-item todo-add-item-if-new-category
764 ;; If the category was added, save the file now, so we 766 cat-added t)
765 ;; don't risk having an empty todo file, which would 767 (if cat-added
766 ;; signal an error if we tried to visit it later, 768 ;; If the category was added, save the file now, so we
767 ;; since doing that looks for category boundaries. 769 ;; don't risk having an empty todo file, which would
768 (save-buffer 0) 770 ;; signal an error if we tried to visit it later,
769 ;; If user cancels before adding the category, clean up 771 ;; since doing that looks for category boundaries.
770 ;; and exit, so we have a fresh slate the next time. 772 (save-buffer 0)
771 (delete-file file) 773 ;; If user cancels before adding the category, clean up
772 (setq todo-files (delete file todo-files)) 774 ;; and exit, so we have a fresh slate the next time.
773 (when first-file 775 (delete-file file)
774 (setq todo-default-todo-file nil 776 ;; (setq todo-files (funcall todo-files-function))
775 todo-current-todo-file nil)) 777 (setq todo-files (delete file todo-files))
776 (kill-buffer) 778 (when first-file
777 (keyboard-quit))))) 779 (setq todo-default-todo-file nil
778 (save-excursion (todo-category-select)) 780 todo-current-todo-file nil)
779 (when add-item (todo-basic-insert-item))) 781 (todo-reevaluate-default-file-defcustom))
780 (setq todo-show-first show-first) 782 (kill-buffer)
781 (add-to-list 'todo-visited file)))) 783 (keyboard-quit)))))
784 (save-excursion (todo-category-select))
785 (when add-item (todo-basic-insert-item)))
786 (setq todo-show-first show-first)
787 (add-to-list 'todo-visited file)))))
782 788
783(defun todo-save () 789(defun todo-save ()
784 "Save the current todo file." 790 "Save the current todo file."
@@ -814,8 +820,15 @@ buries it and restores state as needed."
814 ;; Have to write a newly created archive to file to avoid 820 ;; Have to write a newly created archive to file to avoid
815 ;; subsequent errors. 821 ;; subsequent errors.
816 (todo-save) 822 (todo-save)
817 (todo-show) 823 (let ((todo-file (concat todo-directory
818 (bury-buffer buf)) 824 (todo-short-file-name todo-current-todo-file)
825 ".todo")))
826 (if (todo-check-file todo-file)
827 (todo-show)
828 (message "There is no todo file for this archive")))
829 ;; When todo-check-file runs in todo-show, it kills the
830 ;; buffer if the archive file was deleted externally.
831 (when (buffer-live-p buf) (bury-buffer buf)))
819 ((eq major-mode 'todo-mode) 832 ((eq major-mode 'todo-mode)
820 (todo-save) 833 (todo-save)
821 ;; If we just quit archive mode, just burying the buffer 834 ;; If we just quit archive mode, just burying the buffer
@@ -893,7 +906,7 @@ Categories mode."
893 (interactive "P") 906 (interactive "P")
894 ;; If invoked outside of Todo mode and there is not yet any Todo 907 ;; If invoked outside of Todo mode and there is not yet any Todo
895 ;; file, initialize one. 908 ;; file, initialize one.
896 (if (null todo-files) 909 (if (null (funcall todo-files-function))
897 (todo-show) 910 (todo-show)
898 (let* ((archive (eq where 'archive)) 911 (let* ((archive (eq where 'archive))
899 (cat (unless archive where)) 912 (cat (unless archive where))
@@ -1069,10 +1082,9 @@ option `todo-add-item-if-new-category' is non-nil (the default),
1069prompt for the first item. 1082prompt for the first item.
1070Noninteractively, return the name of the new file." 1083Noninteractively, return the name of the new file."
1071 (interactive) 1084 (interactive)
1072 (let ((prompt (concat "Enter name of new todo file " 1085 (let* ((prompt (concat "Enter name of new todo file "
1073 "(TAB or SPC to see current names): ")) 1086 "(TAB or SPC to see current names): "))
1074 file) 1087 (file (todo-read-file-name prompt)))
1075 (setq file (todo-read-file-name prompt))
1076 (with-current-buffer (get-buffer-create file) 1088 (with-current-buffer (get-buffer-create file)
1077 (erase-buffer) 1089 (erase-buffer)
1078 (write-region (point-min) (point-max) file nil 'nomessage nil t) 1090 (write-region (point-min) (point-max) file nil 'nomessage nil t)
@@ -1087,6 +1099,55 @@ Noninteractively, return the name of the new file."
1087 (todo-show)) 1099 (todo-show))
1088 file))) 1100 file)))
1089 1101
1102(defun todo-delete-file ()
1103 "Delete the current todo, archive or filtered items file.
1104If the todo file has a corresponding archive file, or vice versa,
1105prompt whether to delete that as well. Also kill the buffers
1106visiting the deleted files."
1107 (interactive)
1108 (let* ((file1 (buffer-file-name))
1109 (todo (eq major-mode 'todo-mode))
1110 (archive (eq major-mode 'todo-archive-mode))
1111 (filtered (eq major-mode 'todo-filtered-items-mode))
1112 (file1-sn (todo-short-file-name file1))
1113 (file2 (concat todo-directory file1-sn (cond (todo ".toda")
1114 (archive ".todo"))))
1115 (buf1 (current-buffer))
1116 (buf2 (when file2 (find-buffer-visiting file2)))
1117 (prompt1 (concat "Delete " (cond (todo "todo")
1118 (archive "archive")
1119 (filtered "filtered items"))
1120 " file \"%s\"? "))
1121 (prompt2 (concat "Also delete the corresponding "
1122 (cond (todo "archive") (archive "todo")) " file "
1123 (when buf2 "and kill the buffer visiting it? ")))
1124 (delete1 (yes-or-no-p (format prompt1 file1-sn)))
1125 (delete2 (when (and delete1 (or (file-exists-p file2) buf2))
1126 (yes-or-no-p prompt2))))
1127 (when delete1
1128 (when (file-exists-p file1) (delete-file file1))
1129 (setq todo-visited (delete file1 todo-visited))
1130 (kill-buffer buf1)
1131 (when delete2
1132 (when (file-exists-p file2) (delete-file file2))
1133 (setq todo-visited (delete file2 todo-visited))
1134 (and buf2 (kill-buffer buf2)))
1135 (setq todo-files (funcall todo-files-function)
1136 todo-archives (funcall todo-files-function t))
1137 (when (or (string= file1-sn todo-default-todo-file)
1138 (and delete2 (string= file1-sn todo-default-todo-file)))
1139 (setq todo-default-todo-file (todo-short-file-name (car todo-files))))
1140 (when (or (string= file1 todo-global-current-todo-file)
1141 (and delete2 (string= file2 todo-global-current-todo-file)))
1142 (setq todo-global-current-todo-file nil))
1143 (todo-reevaluate-filelist-defcustoms)
1144 (message (concat (cond (todo "Todo") (archive "Archive")) " file \"%s\" "
1145 (when delete2
1146 (concat "and its "
1147 (cond (todo "archive") (archive "todo"))
1148 " file "))
1149 "deleted") file1-sn))))
1150
1090(defvar todo-edit-buffer "*Todo Edit*" 1151(defvar todo-edit-buffer "*Todo Edit*"
1091 "Name of current buffer in Todo Edit mode.") 1152 "Name of current buffer in Todo Edit mode.")
1092 1153
@@ -1190,9 +1251,9 @@ category there as well."
1190 (save-excursion (todo-category-select))) 1251 (save-excursion (todo-category-select)))
1191 1252
1192(defun todo-delete-category (&optional arg) 1253(defun todo-delete-category (&optional arg)
1193 "Delete current todo category provided it is empty. 1254 "Delete current todo category provided it contains no items.
1194With ARG non-nil delete the category unconditionally, 1255With prefix ARG delete the category even if it does contain
1195i.e. including all existing todo and done items." 1256todo or done items."
1196 (interactive "P") 1257 (interactive "P")
1197 (let* ((file todo-current-todo-file) 1258 (let* ((file todo-current-todo-file)
1198 (cat (todo-current-category)) 1259 (cat (todo-current-category))
@@ -1723,7 +1784,7 @@ the new item:
1723 the item accordingly." 1784 the item accordingly."
1724 ;; If invoked outside of Todo mode and there is not yet any Todo 1785 ;; If invoked outside of Todo mode and there is not yet any Todo
1725 ;; file, initialize one. 1786 ;; file, initialize one.
1726 (if (null todo-files) 1787 (if (null (funcall todo-files-function))
1727 (todo-show) 1788 (todo-show)
1728 (let ((region (eq region-or-here 'region)) 1789 (let ((region (eq region-or-here 'region))
1729 (here (eq region-or-here 'here))) 1790 (here (eq region-or-here 'here)))
@@ -2958,31 +3019,32 @@ first visit in a session displays the first category in the
2958archive, subsequent visits return to the last category 3019archive, subsequent visits return to the last category
2959displayed." 3020displayed."
2960 (interactive) 3021 (interactive)
2961 (let* ((cat (todo-current-category)) 3022 (if (null (funcall todo-files-function t))
2962 (count (todo-get-count 'archived cat)) 3023 (message "There are no archive files")
2963 (archive (concat (file-name-sans-extension todo-current-todo-file) 3024 (let* ((cat (todo-current-category))
2964 ".toda")) 3025 (count (todo-get-count 'archived cat))
2965 place) 3026 (archive (concat (file-name-sans-extension todo-current-todo-file)
2966 (setq place (cond (ask 'other-archive) 3027 ".toda"))
2967 ((file-exists-p archive) 'this-archive) 3028 (place (cond (ask 'other-archive)
2968 (t (when (todo-y-or-n-p 3029 ((file-exists-p archive) 'this-archive)
2969 (concat "This file has no archive; " 3030 (t (when (todo-y-or-n-p
2970 "visit another archive? ")) 3031 (concat "This file has no archive; "
2971 'other-archive)))) 3032 "visit another archive? "))
2972 (when (eq place 'other-archive) 3033 'other-archive)))))
2973 (setq archive (todo-read-file-name "Choose a todo archive: " t t))) 3034 (when (eq place 'other-archive)
2974 (when (and (eq place 'this-archive) (zerop count)) 3035 (setq archive (todo-read-file-name "Choose a todo archive: " t t)))
2975 (setq place (when (todo-y-or-n-p 3036 (when (and (eq place 'this-archive) (zerop count))
2976 (concat "This category has no archived items;" 3037 (setq place (when (todo-y-or-n-p
2977 " visit archive anyway? ")) 3038 (concat "This category has no archived items;"
2978 'other-cat))) 3039 " visit archive anyway? "))
2979 (when place 3040 'other-cat)))
2980 (set-window-buffer (selected-window) 3041 (when place
2981 (set-buffer (find-file-noselect archive))) 3042 (set-window-buffer (selected-window)
2982 (if (member place '(other-archive other-cat)) 3043 (set-buffer (find-file-noselect archive)))
2983 (setq todo-category-number 1) 3044 (if (member place '(other-archive other-cat))
2984 (todo-category-number cat)) 3045 (setq todo-category-number 1)
2985 (todo-category-select)))) 3046 (todo-category-number cat))
3047 (todo-category-select)))))
2986 3048
2987(defun todo-choose-archive () 3049(defun todo-choose-archive ()
2988 "Choose an archive and visit it." 3050 "Choose an archive and visit it."
@@ -3010,9 +3072,7 @@ this category does not exist in the archive, it is created."
3010 (marked (assoc cat todo-categories-with-marks)) 3072 (marked (assoc cat todo-categories-with-marks))
3011 (afile (concat (file-name-sans-extension 3073 (afile (concat (file-name-sans-extension
3012 todo-current-todo-file) ".toda")) 3074 todo-current-todo-file) ".toda"))
3013 (archive (if (file-exists-p afile) 3075 (archive (find-file-noselect afile t))
3014 (find-file-noselect afile t)
3015 (get-buffer-create afile)))
3016 (item (and (todo-done-item-p) 3076 (item (and (todo-done-item-p)
3017 (concat (todo-item-string) "\n"))) 3077 (concat (todo-item-string) "\n")))
3018 (count 0) 3078 (count 0)
@@ -3056,7 +3116,6 @@ this category does not exist in the archive, it is created."
3056 (if (not (or marked all item)) 3116 (if (not (or marked all item))
3057 (throw 'end (message "Only done items can be archived")) 3117 (throw 'end (message "Only done items can be archived"))
3058 (with-current-buffer archive 3118 (with-current-buffer archive
3059 (unless buffer-file-name (erase-buffer))
3060 (let (buffer-read-only) 3119 (let (buffer-read-only)
3061 (widen) 3120 (widen)
3062 (goto-char (point-min)) 3121 (goto-char (point-min))
@@ -3076,11 +3135,13 @@ this category does not exist in the archive, it is created."
3076 (item))) 3135 (item)))
3077 (todo-update-count 'done (if (or marked all) count 1) cat) 3136 (todo-update-count 'done (if (or marked all) count 1) cat)
3078 (todo-update-categories-sexp) 3137 (todo-update-categories-sexp)
3079 ;; If archive is new, save to file now (using write-region in 3138 ;; If archive is new, save to file now (with
3080 ;; order not to get prompted for file to save to), to let 3139 ;; write-region to avoid prompt for file to save to)
3081 ;; auto-mode-alist take effect below. 3140 ;; to update todo-archives, and to let auto-mode-alist
3082 (unless buffer-file-name 3141 ;; take effect below on visiting the archive.
3083 (write-region nil nil afile) 3142 (unless (nth 7 (file-attributes afile))
3143 (write-region nil nil afile t t)
3144 (setq todo-archives (funcall todo-files-function t))
3084 (kill-buffer)))) 3145 (kill-buffer))))
3085 (with-current-buffer tbuf 3146 (with-current-buffer tbuf
3086 (cond 3147 (cond
@@ -3286,19 +3347,24 @@ categories display according to priority."
3286(defun todo-show-categories-table () 3347(defun todo-show-categories-table ()
3287 "Display a table of the current file's categories and item counts. 3348 "Display a table of the current file's categories and item counts.
3288 3349
3289In the initial display the categories are numbered, indicating 3350In the initial display the lines of the table are numbered,
3290their current order for navigating by \\[todo-forward-category] 3351indicating the current order of the categories when sequentially
3291and \\[todo-backward-category]. You can permanently change the 3352navigating through the todo file with `\\[todo-forward-category]'
3292order of the category at point by typing 3353and `\\[todo-backward-category]'. You can reorder the lines, and
3293\\[todo-set-category-number], \\[todo-raise-category] or 3354hence the category sequence, by typing `\\[todo-raise-category]'
3294\\[todo-lower-category]. 3355or `\\[todo-lower-category]' to raise or lower the category at
3356point, or by typing `\\[todo-set-category-number]' and entering a
3357number at the prompt or by typing `\\[todo-set-category-number]'
3358with a numeric prefix. If you save the todo file after
3359reordering the categories, the new order persists in subsequent
3360Emacs sessions.
3295 3361
3296The labels above the category names and item counts are buttons, 3362The labels above the category names and item counts are buttons,
3297and clicking these changes the display: sorted by category name 3363and clicking these changes the display: sorted by category name
3298or by the respective item counts (alternately descending or 3364or by the respective item counts (alternately descending or
3299ascending). In these displays the categories are not numbered 3365ascending). In these displays the categories are not numbered
3300and \\[todo-set-category-number], \\[todo-raise-category] and 3366and `\\[todo-set-category-number]', `\\[todo-raise-category]' and
3301\\[todo-lower-category] are disabled. (Programmatically, the 3367`\\[todo-lower-category]' are disabled. (Programmatically, the
3302sorting is triggered by passing a non-nil SORTKEY argument.) 3368sorting is triggered by passing a non-nil SORTKEY argument.)
3303 3369
3304In addition, the lines with the category names and item counts 3370In addition, the lines with the category names and item counts
@@ -4019,15 +4085,15 @@ regexp items."
4019 "Buffer type string for `todo-filter-items'.") 4085 "Buffer type string for `todo-filter-items'.")
4020 4086
4021(defun todo-filter-items (filter &optional new multifile) 4087(defun todo-filter-items (filter &optional new multifile)
4022 "Display a cross-category list of items filtered by FILTER. 4088 "Display a list of items filtered by FILTER.
4023The values of FILTER can be `top' for top priority items, a cons 4089The values of FILTER can be `top' for top priority items, a cons
4024of `top' and a number passed by the caller, `diary' for diary 4090of `top' and a number passed by the caller, `diary' for diary
4025items, or `regexp' for items matching a regular expression entered 4091items, or `regexp' for items matching a regular expression
4026by the user. The items can be from any categories in the current 4092entered by the user. The items can come from any categories in
4027todo file or, with non-nil MULTIFILE, from several files. If NEW 4093the current todo file or, with non-nil MULTIFILE, from several
4028is nil, visit an appropriate file containing the list of filtered 4094files. If NEW is nil, visit an appropriate file containing the
4029items; if there is no such file, or with non-nil NEW, build the 4095list of filtered items; if there is no such file, or with non-nil
4030list and display it. 4096NEW, build the list and display it.
4031 4097
4032See the documentation strings of the commands 4098See the documentation strings of the commands
4033`todo-filter-top-priorities', `todo-filter-diary-items', 4099`todo-filter-top-priorities', `todo-filter-diary-items',
@@ -4699,14 +4765,57 @@ short todo archive or top priorities file name, respectively."
4699 ((eq type 'regexp) ".todr") 4765 ((eq type 'regexp) ".todr")
4700 (t ".todo")))))) 4766 (t ".todo"))))))
4701 4767
4768(defun todo-check-file (file)
4769 "Check the state associated with FILE and update it if necessary.
4770If FILE exists, return t. If it does not exist and there is no
4771live buffer with its content, return nil; if there is such a
4772buffer and the user tries to show it, ask whether to restore
4773FILE, and if confirmed, do so and return t; else delete the
4774buffer, clean up the state and return nil."
4775 (setq todo-files (funcall todo-files-function))
4776 (setq todo-archives (funcall todo-files-function t))
4777 (if (file-exists-p file)
4778 t
4779 (setq todo-visited (delete file todo-visited))
4780 (let ((buf (find-buffer-visiting file)))
4781 (if (and buf
4782 (y-or-n-p
4783 (concat
4784 (format (concat "Todo file \"%s\" has been deleted but "
4785 "its content is still in a buffer!\n")
4786 (todo-short-file-name file))
4787 "Save that buffer and restore the todo file? ")))
4788 (progn
4789 (with-current-buffer buf (save-buffer))
4790 (setq todo-files (funcall todo-files-function))
4791 (setq todo-archives (funcall todo-files-function t))
4792 t)
4793 (let* ((files (append todo-files todo-archives))
4794 (tctf todo-current-todo-file)
4795 (tgctf todo-global-current-todo-file)
4796 (tdtf (todo-absolute-file-name todo-default-todo-file)))
4797 (unless (or (not todo-current-todo-file)
4798 (member todo-current-todo-file files))
4799 (setq todo-current-todo-file nil))
4800 (unless (or (not todo-global-current-todo-file)
4801 (member todo-global-current-todo-file files))
4802 (setq todo-global-current-todo-file nil))
4803 (unless (or (not todo-default-todo-file)
4804 (member todo-default-todo-file files))
4805 (setq todo-default-todo-file (todo-short-file-name
4806 (car todo-files))))
4807 (todo-reevaluate-filelist-defcustoms)
4808 (when buf (kill-buffer buf))
4809 nil)))))
4810
4702(defun todo-category-number (cat) 4811(defun todo-category-number (cat)
4703 "Return the number of category CAT in this todo file. 4812 "Return the number of category CAT in this todo file.
4704The buffer-local variable `todo-category-number' holds this 4813The buffer-local variable `todo-category-number' holds this
4705number as its value." 4814number as its value."
4706 (let ((categories (mapcar 'car todo-categories))) 4815 (let ((categories (mapcar 'car todo-categories)))
4707 (setq todo-category-number 4816 (setq todo-category-number
4708 ;; Increment by one, so that the highest priority category in Todo 4817 ;; Increment by one, so that the number of the first
4709 ;; Categories mode is numbered one rather than zero. 4818 ;; category is one rather than zero.
4710 (1+ (- (length categories) 4819 (1+ (- (length categories)
4711 (length (member cat categories))))))) 4820 (length (member cat categories)))))))
4712 4821
@@ -5384,7 +5493,27 @@ Each element of the list is a cons of a category name and the
5384file or list of files (as short file names) it is in. The files 5493file or list of files (as short file names) it is in. The files
5385are either the current (or if there is none, the default) todo 5494are either the current (or if there is none, the default) todo
5386file plus the files listed in `todo-category-completions-files', 5495file plus the files listed in `todo-category-completions-files',
5387or, with non-nil ARCHIVE, the current archive file." 5496or, with non-nil ARCHIVE, the current archive file.
5497
5498Before calculating the completions, update the value of
5499`todo-category-completions-files' in case any files named in it
5500have been removed."
5501 (let (deleted)
5502 (dolist (f todo-category-completions-files)
5503 (unless (file-exists-p (todo-absolute-file-name f))
5504 (setq todo-category-completions-files
5505 (delete f todo-category-completions-files))
5506 (push f deleted)))
5507 (when deleted
5508 (let ((pl (> (length deleted) 1))
5509 (names (mapconcat (lambda (f) (concat "\"" f "\"")) deleted ", ")))
5510 (message (concat "File" (if pl "s" "") " " names " ha" (if pl "ve" "s")
5511 " been deleted and removed from\n"
5512 "the list of category completion files")))
5513 (todo-reevaluate-category-completions-files-defcustom)
5514 (custom-set-default 'todo-category-completions-files
5515 (symbol-value 'todo-category-completions-files))
5516 (sleep-for 1.5)))
5388 (let* ((curfile (or todo-current-todo-file 5517 (let* ((curfile (or todo-current-todo-file
5389 (and todo-show-current-file 5518 (and todo-show-current-file
5390 todo-global-current-todo-file) 5519 todo-global-current-todo-file)
@@ -5435,6 +5564,7 @@ MUSTMATCH the name of an existing file must be chosen;
5435otherwise, a new file name is allowed." 5564otherwise, a new file name is allowed."
5436 (let* ((completion-ignore-case todo-completion-ignore-case) 5565 (let* ((completion-ignore-case todo-completion-ignore-case)
5437 (files (mapcar 'todo-short-file-name 5566 (files (mapcar 'todo-short-file-name
5567 ;; (funcall todo-files-function archive)))
5438 (if archive todo-archives todo-files))) 5568 (if archive todo-archives todo-files)))
5439 (file (completing-read prompt files nil mustmatch nil nil 5569 (file (completing-read prompt files nil mustmatch nil nil
5440 (if files 5570 (if files
@@ -5529,7 +5659,7 @@ categories from `todo-category-completions-files'."
5529 ;; Validate only against completion categories. 5659 ;; Validate only against completion categories.
5530 (let ((todo-categories categories)) 5660 (let ((todo-categories categories))
5531 (setq cat (todo-validate-name cat 'category))) 5661 (setq cat (todo-validate-name cat 'category)))
5532 ;; When user enters a nonexistest category name by jumping or 5662 ;; When user enters a nonexistent category name by jumping or
5533 ;; moving, confirm that it should be added, then validate. 5663 ;; moving, confirm that it should be added, then validate.
5534 (unless add 5664 (unless add
5535 (if (todo-y-or-n-p (format "Add new category \"%s\" to file \"%s\"? " 5665 (if (todo-y-or-n-p (format "Add new category \"%s\" to file \"%s\"? "
@@ -5867,13 +5997,24 @@ the empty string (i.e., no time string)."
5867 5997
5868(defun todo-reevaluate-default-file-defcustom () 5998(defun todo-reevaluate-default-file-defcustom ()
5869 "Reevaluate defcustom of `todo-default-todo-file'. 5999 "Reevaluate defcustom of `todo-default-todo-file'.
5870Called after adding or deleting a todo file." 6000Called after adding or deleting a todo file. If the value of
5871 (eval (defcustom todo-default-todo-file (car (funcall todo-files-function)) 6001`todo-default-todo-file' before calling this function was
5872 "Todo file visited by first session invocation of `todo-show'." 6002associated with an existing file, keep that value."
5873 :type `(radio ,@(mapcar (lambda (f) (list 'const f)) 6003 ;; (let ((curval todo-default-todo-file))
5874 (mapcar 'todo-short-file-name 6004 (eval
5875 (funcall todo-files-function)))) 6005 (defcustom todo-default-todo-file (todo-short-file-name
5876 :group 'todo))) 6006 (car (funcall todo-files-function)))
6007 "Todo file visited by first session invocation of `todo-show'."
6008 :type (when todo-files
6009 `(radio ,@(mapcar (lambda (f) (list 'const f))
6010 (mapcar 'todo-short-file-name
6011 (funcall todo-files-function)))))
6012 :group 'todo))
6013 ;; (when (and curval (file-exists-p (todo-absolute-file-name curval)))
6014 ;; (custom-set-default 'todo-default-todo-file curval)
6015 ;; ;; (custom-reevaluate-setting 'todo-default-todo-file)
6016 ;; )))
6017 )
5877 6018
5878(defun todo-reevaluate-category-completions-files-defcustom () 6019(defun todo-reevaluate-category-completions-files-defcustom ()
5879 "Reevaluate defcustom of `todo-category-completions-files'. 6020 "Reevaluate defcustom of `todo-category-completions-files'.
@@ -6060,6 +6201,7 @@ Filtered Items mode following todo (not done) items."
6060 ("Cu" todo-unmark-category) 6201 ("Cu" todo-unmark-category)
6061 ("Fh" todo-toggle-item-header) 6202 ("Fh" todo-toggle-item-header)
6062 ("h" todo-toggle-item-header) 6203 ("h" todo-toggle-item-header)
6204 ("Fk" todo-delete-file)
6063 ("Fe" todo-edit-file) 6205 ("Fe" todo-edit-file)
6064 ("FH" todo-toggle-item-highlighting) 6206 ("FH" todo-toggle-item-highlighting)
6065 ("H" todo-toggle-item-highlighting) 6207 ("H" todo-toggle-item-highlighting)
@@ -6226,12 +6368,13 @@ Filtered Items mode following todo (not done) items."
6226 6368
6227(defun todo-show-current-file () 6369(defun todo-show-current-file ()
6228 "Visit current instead of default todo file with `todo-show'. 6370 "Visit current instead of default todo file with `todo-show'.
6229This function is added to `pre-command-hook' when user option 6371Added to `pre-command-hook' in Todo mode when user option
6230`todo-show-current-file' is set to non-nil." 6372`todo-show-current-file' is set to non-nil."
6231 (setq todo-global-current-todo-file todo-current-todo-file)) 6373 (setq todo-global-current-todo-file todo-current-todo-file))
6232 6374
6233(defun todo-display-as-todo-file () 6375(defun todo-display-as-todo-file ()
6234 "Show todo files correctly when visited from outside of Todo mode." 6376 "Show todo files correctly when visited from outside of Todo mode.
6377Added to `find-file-hook' in Todo mode and Todo Archive mode."
6235 (and (member this-command todo-visit-files-commands) 6378 (and (member this-command todo-visit-files-commands)
6236 (= (- (point-max) (point-min)) (buffer-size)) 6379 (= (- (point-max) (point-min)) (buffer-size))
6237 (member major-mode '(todo-mode todo-archive-mode)) 6380 (member major-mode '(todo-mode todo-archive-mode))
@@ -6265,7 +6408,7 @@ This function is added to `kill-buffer-hook' in Todo mode."
6265 6408
6266(defun todo-reset-and-enable-done-separator () 6409(defun todo-reset-and-enable-done-separator ()
6267 "Show resized done items separator overlay after window change. 6410 "Show resized done items separator overlay after window change.
6268Added to `window-configuration-change-hook' in `todo-mode'." 6411Added to `window-configuration-change-hook' in Todo mode."
6269 (when (= 1 (length todo-done-separator-string)) 6412 (when (= 1 (length todo-done-separator-string))
6270 (let ((sep todo-done-separator)) 6413 (let ((sep todo-done-separator))
6271 (setq todo-done-separator (todo-done-separator)) 6414 (setq todo-done-separator (todo-done-separator))