aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Berman2013-06-19 21:59:26 +0200
committerStephen Berman2013-06-19 21:59:26 +0200
commitddce2e3ed10ae39db765dbc66322cde94e0d6973 (patch)
tree2862b0cc1835612ddcf29c3bbaf1ef02fd4f73b5
parent18b4baa8a4659a1208c928b47fbd9d264073a743 (diff)
downloademacs-ddce2e3ed10ae39db765dbc66322cde94e0d6973.tar.gz
emacs-ddce2e3ed10ae39db765dbc66322cde94e0d6973.zip
* todo-mode.el: Rename from todos.el, change all occurrences of
"todos-" prefix to "todo-" and references to "Todos" to "Todo" or "Todo mode".
-rw-r--r--lisp/calendar/ChangeLog6
-rw-r--r--lisp/calendar/todo-mode.el (renamed from lisp/calendar/todos.el)4538
2 files changed, 2275 insertions, 2269 deletions
diff --git a/lisp/calendar/ChangeLog b/lisp/calendar/ChangeLog
index e96c9870c11..4792cbb336f 100644
--- a/lisp/calendar/ChangeLog
+++ b/lisp/calendar/ChangeLog
@@ -1,5 +1,11 @@
12013-06-19 Stephen Berman <stephen.berman@gmx.net> 12013-06-19 Stephen Berman <stephen.berman@gmx.net>
2 2
3 * todo-mode.el: Rename from todos.el, change all occurrences of
4 "todos-" prefix to "todo-" and references to "Todos" to "Todo" or
5 "Todo mode".
6
72013-06-19 Stephen Berman <stephen.berman@gmx.net>
8
3 * calendar/todo-mode.el: Rename to otodo-mode.el and move to obsolete/. 9 * calendar/todo-mode.el: Rename to otodo-mode.el and move to obsolete/.
4 10
52013-06-19 Stephen Berman <stephen.berman@gmx.net> 112013-06-19 Stephen Berman <stephen.berman@gmx.net>
diff --git a/lisp/calendar/todos.el b/lisp/calendar/todo-mode.el
index 667b69a66f3..12c9fc6973b 100644
--- a/lisp/calendar/todos.el
+++ b/lisp/calendar/todo-mode.el
@@ -1,4 +1,4 @@
1;;; todos.el --- facilities for making and maintaining todo lists 1;;; todo-mode.el --- facilities for making and maintaining todo lists
2 2
3;; Copyright (C) 1997, 1999, 2001-2013 Free Software Foundation, Inc. 3;; Copyright (C) 1997, 1999, 2001-2013 Free Software Foundation, Inc.
4 4
@@ -32,7 +32,7 @@
32;; level of organization. You can create as many todo files, and in 32;; level of organization. You can create as many todo files, and in
33;; each as many categories, as you want. 33;; each as many categories, as you want.
34 34
35;; With Todos you can navigate among the items of a category, and 35;; With Todo mode you can navigate among the items of a category, and
36;; between categories in the same and in different todo files. You 36;; between categories in the same and in different todo files. You
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
@@ -43,21 +43,21 @@
43;; contain. And you can build cross-categorial lists of items that 43;; contain. And you can build cross-categorial lists of items that
44;; satisfy various criteria. 44;; satisfy various criteria.
45 45
46;; To get started, load this package and type `M-x todos-show'. This 46;; 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 47;; will prompt you for the name of the first todo file, its first
48;; category and the category's first item, create these and display 48;; category and the category's first item, create these and display
49;; them in Todos mode. Now you can insert further items into the list 49;; them in Todo mode. Now you can insert further items into the list
50;; (i.e., the category) and assign them priorities by typing `i i'. 50;; (i.e., the category) and assign them priorities by typing `i i'.
51 51
52;; You will probably find it convenient to give `todos-show' a global 52;; You will probably find it convenient to give `todo-show' a global
53;; key binding in your init file, since it is one of the entry points 53;; key binding in your init file, since it is one of the entry points
54;; to Todos mode; a good choice is `C-c t', since `todos-show' is 54;; to Todo mode; a good choice is `C-c t', since `todo-show' is
55;; bound to `t' in Todos mode. 55;; bound to `t' in Todo mode.
56 56
57;; To see a list of all Todos mode commands and their key bindings, 57;; To see a list of all Todo mode commands and their key bindings,
58;; including other entry points, type `C-h m' in Todos mode. Consult 58;; including other entry points, type `C-h m' in Todo mode. Consult
59;; the document strings of the commands for details of their use. The 59;; the document strings of the commands for details of their use. The
60;; `todos' customization group and its subgroups list the options you 60;; `todo' customization group and its subgroups list the options you
61;; can set to alter the behavior of many commands and various aspects 61;; can set to alter the behavior of many commands and various aspects
62;; of the display. 62;; of the display.
63 63
@@ -66,244 +66,244 @@
66;; lists and the basic UI, it significantly extends these and adds 66;; lists and the basic UI, it significantly extends these and adds
67;; many features. This also required making changes to the internals, 67;; many features. This also required making changes to the internals,
68;; including the file format. To convert files in the old format to 68;; including the file format. To convert files in the old format to
69;; the new format, use the command `todos-convert-legacy-files'. 69;; the new format, use the command `todo-convert-legacy-files'.
70 70
71;;; Code: 71;;; Code:
72 72
73(require 'diary-lib) 73(require 'diary-lib)
74;; For cl-remove-duplicates (in todos-insertion-commands-args) and 74;; For cl-remove-duplicates (in todo-insertion-commands-args) and
75;; cl-oddp. 75;; cl-oddp.
76(require 'cl-lib) 76(require 'cl-lib)
77 77
78;; ----------------------------------------------------------------------------- 78;; -----------------------------------------------------------------------------
79;;; Setting up Todos files, categories, and items 79;;; Setting up Todo files, categories, and items
80;; ----------------------------------------------------------------------------- 80;; -----------------------------------------------------------------------------
81 81
82(defcustom todos-directory (locate-user-emacs-file "todos/") 82(defcustom todo-directory (locate-user-emacs-file "todo/")
83 "Directory where user's Todos files are saved." 83 "Directory where user's Todo files are saved."
84 :type 'directory 84 :type 'directory
85 :group 'todos) 85 :group 'todo)
86 86
87(defun todos-files (&optional archives) 87(defun todo-files (&optional archives)
88 "Default value of `todos-files-function'. 88 "Default value of `todo-files-function'.
89This returns the case-insensitive alphabetically sorted list of 89This returns the case-insensitive alphabetically sorted list of
90file truenames in `todos-directory' with the extension 90file truenames in `todo-directory' with the extension
91\".todo\". With non-nil ARCHIVES return the list of archive file 91\".todo\". With non-nil ARCHIVES return the list of archive file
92truenames (those with the extension \".toda\")." 92truenames (those with the extension \".toda\")."
93 (let ((files (if (file-exists-p todos-directory) 93 (let ((files (if (file-exists-p todo-directory)
94 (mapcar 'file-truename 94 (mapcar 'file-truename
95 (directory-files todos-directory t 95 (directory-files todo-directory t
96 (if archives "\.toda$" "\.todo$") t))))) 96 (if archives "\.toda$" "\.todo$") t)))))
97 (sort files (lambda (s1 s2) (let ((cis1 (upcase s1)) 97 (sort files (lambda (s1 s2) (let ((cis1 (upcase s1))
98 (cis2 (upcase s2))) 98 (cis2 (upcase s2)))
99 (string< cis1 cis2)))))) 99 (string< cis1 cis2))))))
100 100
101(defcustom todos-files-function 'todos-files 101(defcustom todo-files-function 'todo-files
102 "Function returning the value of the variable `todos-files'. 102 "Function returning the value of the variable `todo-files'.
103This function should take an optional argument that, if non-nil, 103This function should take an optional argument that, if non-nil,
104makes it return the value of the variable `todos-archives'." 104makes it return the value of the variable `todo-archives'."
105 :type 'function 105 :type 'function
106 :group 'todos) 106 :group 'todo)
107 107
108(defvar todos-files (funcall todos-files-function) 108(defvar todo-files (funcall todo-files-function)
109 "List of truenames of user's Todos files.") 109 "List of truenames of user's Todo files.")
110 110
111(defvar todos-archives (funcall todos-files-function t) 111(defvar todo-archives (funcall todo-files-function t)
112 "List of truenames of user's Todos archives.") 112 "List of truenames of user's Todo archives.")
113 113
114(defvar todos-visited nil 114(defvar todo-visited nil
115 "List of Todos files visited in this session by `todos-show'. 115 "List of Todo files visited in this session by `todo-show'.
116Used to determine initial display according to the value of 116Used to determine initial display according to the value of
117`todos-show-first'.") 117`todo-show-first'.")
118 118
119(defvar todos-file-buffers nil 119(defvar todo-file-buffers nil
120 "List of file names of live Todos mode buffers.") 120 "List of file names of live Todo mode buffers.")
121 121
122(defvar todos-global-current-todos-file nil 122(defvar todo-global-current-todo-file nil
123 "Variable holding name of current Todos file. 123 "Variable holding name of current Todo file.
124Used by functions called from outside of Todos mode to visit the 124Used by functions called from outside of Todo mode to visit the
125current Todos file rather than the default Todos file (i.e. when 125current Todo file rather than the default Todo file (i.e. when
126users option `todos-show-current-file' is non-nil).") 126users option `todo-show-current-file' is non-nil).")
127 127
128(defvar todos-current-todos-file nil 128(defvar todo-current-todo-file nil
129 "Variable holding the name of the currently active Todos file.") 129 "Variable holding the name of the currently active Todo file.")
130 130
131(defvar todos-categories nil 131(defvar todo-categories nil
132 "Alist of categories in the current Todos file. 132 "Alist of categories in the current Todo file.
133The elements are cons cells whose car is a category name and 133The elements are cons cells whose car is a category name and
134whose cdr is a vector of the category's item counts. These are, 134whose cdr is a vector of the category's item counts. These are,
135in order, the numbers of todo items, of todo items included in 135in order, the numbers of todo items, of todo items included in
136the Diary, of done items and of archived items.") 136the Diary, of done items and of archived items.")
137 137
138(defvar todos-category-number 1 138(defvar todo-category-number 1
139 "Variable holding the number of the current Todos category. 139 "Variable holding the number of the current Todo category.
140Todos categories are numbered starting from 1.") 140Todo categories are numbered starting from 1.")
141 141
142(defvar todos-categories-with-marks nil 142(defvar todo-categories-with-marks nil
143 "Alist of categories and number of marked items they contain.") 143 "Alist of categories and number of marked items they contain.")
144 144
145(defconst todos-category-beg "--==-- " 145(defconst todo-category-beg "--==-- "
146 "String marking beginning of category (inserted with its name).") 146 "String marking beginning of category (inserted with its name).")
147 147
148(defconst todos-category-done "==--== DONE " 148(defconst todo-category-done "==--== DONE "
149 "String marking beginning of category's done items.") 149 "String marking beginning of category's done items.")
150 150
151(defcustom todos-done-separator-string "=" 151(defcustom todo-done-separator-string "="
152 "String determining the value of variable `todos-done-separator'. 152 "String determining the value of variable `todo-done-separator'.
153If the string consists of a single character, 153If the string consists of a single character,
154`todos-done-separator' will be the string made by repeating this 154`todo-done-separator' will be the string made by repeating this
155character for the width of the window, and the length is 155character for the width of the window, and the length is
156automatically recalculated when the window width changes. If the 156automatically recalculated when the window width changes. If the
157string consists of more (or less) than one character, it will be 157string consists of more (or less) than one character, it will be
158the value of `todos-done-separator'." 158the value of `todo-done-separator'."
159 :type 'string 159 :type 'string
160 :initialize 'custom-initialize-default 160 :initialize 'custom-initialize-default
161 :set 'todos-reset-done-separator-string 161 :set 'todo-reset-done-separator-string
162 :group 'todos-display) 162 :group 'todo-display)
163 163
164(defun todos-done-separator () 164(defun todo-done-separator ()
165 "Return string used as value of variable `todos-done-separator'." 165 "Return string used as value of variable `todo-done-separator'."
166 (let ((sep todos-done-separator-string)) 166 (let ((sep todo-done-separator-string))
167 (propertize (if (= 1 (length sep)) 167 (propertize (if (= 1 (length sep))
168 ;; Until bug#2749 is fixed, if separator's length 168 ;; Until bug#2749 is fixed, if separator's length
169 ;; is window-width and todos-wrap-lines is 169 ;; is window-width and todo-wrap-lines is
170 ;; non-nil, an indented empty line appears between 170 ;; non-nil, an indented empty line appears between
171 ;; the separator and the first done item. 171 ;; the separator and the first done item.
172 ;; (make-string (window-width) (string-to-char sep)) 172 ;; (make-string (window-width) (string-to-char sep))
173 (make-string (1- (window-width)) (string-to-char sep)) 173 (make-string (1- (window-width)) (string-to-char sep))
174 todos-done-separator-string) 174 todo-done-separator-string)
175 'face 'todos-done-sep))) 175 'face 'todo-done-sep)))
176 176
177(defvar todos-done-separator (todos-done-separator) 177(defvar todo-done-separator (todo-done-separator)
178 "String used to visually separate done from not done items. 178 "String used to visually separate done from not done items.
179Displayed as an overlay instead of `todos-category-done' when 179Displayed as an overlay instead of `todo-category-done' when
180done items are shown. Its value is determined by user option 180done items are shown. Its value is determined by user option
181`todos-done-separator-string'.") 181`todo-done-separator-string'.")
182 182
183(defvar todos-show-done-only nil 183(defvar todo-show-done-only nil
184 "If non-nil display only done items in current category. 184 "If non-nil display only done items in current category.
185Set by the command `todos-toggle-view-done-only' and used by 185Set by the command `todo-toggle-view-done-only' and used by
186`todos-category-select'.") 186`todo-category-select'.")
187 187
188(defcustom todos-nondiary-marker '("[" "]") 188(defcustom todo-nondiary-marker '("[" "]")
189 "List of strings surrounding item date to block diary inclusion. 189 "List of strings surrounding item date to block diary inclusion.
190The first string is inserted before the item date and must be a 190The first string is inserted before the item date and must be a
191non-empty string that does not match a diary date in order to 191non-empty string that does not match a diary date in order to
192have its intended effect. The second string is inserted after 192have its intended effect. The second string is inserted after
193the diary date." 193the diary date."
194 :type '(list string string) 194 :type '(list string string)
195 :group 'todos-edit 195 :group 'todo-edit
196 :initialize 'custom-initialize-default 196 :initialize 'custom-initialize-default
197 :set 'todos-reset-nondiary-marker) 197 :set 'todo-reset-nondiary-marker)
198 198
199(defconst todos-nondiary-start (nth 0 todos-nondiary-marker) 199(defconst todo-nondiary-start (nth 0 todo-nondiary-marker)
200 "String inserted before item date to block diary inclusion.") 200 "String inserted before item date to block diary inclusion.")
201 201
202(defconst todos-nondiary-end (nth 1 todos-nondiary-marker) 202(defconst todo-nondiary-end (nth 1 todo-nondiary-marker)
203 "String inserted after item date matching `todos-nondiary-start'.") 203 "String inserted after item date matching `todo-nondiary-start'.")
204 204
205(defconst todos-month-name-array 205(defconst todo-month-name-array
206 (vconcat calendar-month-name-array (vector "*")) 206 (vconcat calendar-month-name-array (vector "*"))
207 "Array of month names, in order. 207 "Array of month names, in order.
208The final element is \"*\", indicating an unspecified month.") 208The final element is \"*\", indicating an unspecified month.")
209 209
210(defconst todos-month-abbrev-array 210(defconst todo-month-abbrev-array
211 (vconcat calendar-month-abbrev-array (vector "*")) 211 (vconcat calendar-month-abbrev-array (vector "*"))
212 "Array of abbreviated month names, in order. 212 "Array of abbreviated month names, in order.
213The final element is \"*\", indicating an unspecified month.") 213The final element is \"*\", indicating an unspecified month.")
214 214
215(defconst todos-date-pattern 215(defconst todo-date-pattern
216 (let ((dayname (diary-name-pattern calendar-day-name-array nil t))) 216 (let ((dayname (diary-name-pattern calendar-day-name-array nil t)))
217 (concat "\\(?4:\\(?5:" dayname "\\)\\|" 217 (concat "\\(?4:\\(?5:" dayname "\\)\\|"
218 (let ((dayname) 218 (let ((dayname)
219 (monthname (format "\\(?6:%s\\)" (diary-name-pattern 219 (monthname (format "\\(?6:%s\\)" (diary-name-pattern
220 todos-month-name-array 220 todo-month-name-array
221 todos-month-abbrev-array))) 221 todo-month-abbrev-array)))
222 (month "\\(?7:[0-9]+\\|\\*\\)") 222 (month "\\(?7:[0-9]+\\|\\*\\)")
223 (day "\\(?8:[0-9]+\\|\\*\\)") 223 (day "\\(?8:[0-9]+\\|\\*\\)")
224 (year "-?\\(?9:[0-9]+\\|\\*\\)")) 224 (year "-?\\(?9:[0-9]+\\|\\*\\)"))
225 (mapconcat 'eval calendar-date-display-form "")) 225 (mapconcat 'eval calendar-date-display-form ""))
226 "\\)")) 226 "\\)"))
227 "Regular expression matching a Todos date header.") 227 "Regular expression matching a Todo date header.")
228 228
229;; By itself this matches anything, because of the `?'; however, it's only 229;; By itself this matches anything, because of the `?'; however, it's only
230;; used in the context of `todos-date-pattern' (but Emacs Lisp lacks 230;; used in the context of `todo-date-pattern' (but Emacs Lisp lacks
231;; lookahead). 231;; lookahead).
232(defconst todos-date-string-start 232(defconst todo-date-string-start
233 (concat "^\\(" (regexp-quote todos-nondiary-start) "\\|" 233 (concat "^\\(" (regexp-quote todo-nondiary-start) "\\|"
234 (regexp-quote diary-nonmarking-symbol) "\\)?") 234 (regexp-quote diary-nonmarking-symbol) "\\)?")
235 "Regular expression matching part of item header before the date.") 235 "Regular expression matching part of item header before the date.")
236 236
237(defcustom todos-done-string "DONE " 237(defcustom todo-done-string "DONE "
238 "Identifying string appended to the front of done todos items." 238 "Identifying string appended to the front of done todo items."
239 :type 'string 239 :type 'string
240 :initialize 'custom-initialize-default 240 :initialize 'custom-initialize-default
241 :set 'todos-reset-done-string 241 :set 'todo-reset-done-string
242 :group 'todos-edit) 242 :group 'todo-edit)
243 243
244(defconst todos-done-string-start 244(defconst todo-done-string-start
245 (concat "^\\[" (regexp-quote todos-done-string)) 245 (concat "^\\[" (regexp-quote todo-done-string))
246 "Regular expression matching start of done item.") 246 "Regular expression matching start of done item.")
247 247
248(defconst todos-item-start (concat "\\(" todos-date-string-start "\\|" 248(defconst todo-item-start (concat "\\(" todo-date-string-start "\\|"
249 todos-done-string-start "\\)" 249 todo-done-string-start "\\)"
250 todos-date-pattern) 250 todo-date-pattern)
251 "String identifying start of a Todos item.") 251 "String identifying start of a Todo item.")
252 252
253;; ----------------------------------------------------------------------------- 253;; -----------------------------------------------------------------------------
254;;; Todos mode display options 254;;; Todo mode display options
255;; ----------------------------------------------------------------------------- 255;; -----------------------------------------------------------------------------
256 256
257(defcustom todos-prefix "" 257(defcustom todo-prefix ""
258 "String prefixed to todo items for visual distinction." 258 "String prefixed to todo items for visual distinction."
259 :type '(string :validate 259 :type '(string :validate
260 (lambda (widget) 260 (lambda (widget)
261 (when (string= (widget-value widget) todos-item-mark) 261 (when (string= (widget-value widget) todo-item-mark)
262 (widget-put 262 (widget-put
263 widget :error 263 widget :error
264 "Invalid value: must be distinct from `todos-item-mark'") 264 "Invalid value: must be distinct from `todo-item-mark'")
265 widget))) 265 widget)))
266 :initialize 'custom-initialize-default 266 :initialize 'custom-initialize-default
267 :set 'todos-reset-prefix 267 :set 'todo-reset-prefix
268 :group 'todos-display) 268 :group 'todo-display)
269 269
270(defcustom todos-number-prefix t 270(defcustom todo-number-prefix t
271 "Non-nil to prefix items with consecutively increasing integers. 271 "Non-nil to prefix items with consecutively increasing integers.
272These reflect the priorities of the items in each category." 272These reflect the priorities of the items in each category."
273 :type 'boolean 273 :type 'boolean
274 :initialize 'custom-initialize-default 274 :initialize 'custom-initialize-default
275 :set 'todos-reset-prefix 275 :set 'todo-reset-prefix
276 :group 'todos-display) 276 :group 'todo-display)
277 277
278(defun todos-mode-line-control (cat) 278(defun todo-mode-line-control (cat)
279 "Return a mode line control for todo or archive file buffers. 279 "Return a mode line control for todo or archive file buffers.
280Argument CAT is the name of the current Todos category. 280Argument CAT is the name of the current Todo category.
281This function is the value of the user variable 281This function is the value of the user variable
282`todos-mode-line-function'." 282`todo-mode-line-function'."
283 (let ((file (todos-short-file-name todos-current-todos-file))) 283 (let ((file (todo-short-file-name todo-current-todo-file)))
284 (format "%s category %d: %s" file todos-category-number cat))) 284 (format "%s category %d: %s" file todo-category-number cat)))
285 285
286(defcustom todos-mode-line-function 'todos-mode-line-control 286(defcustom todo-mode-line-function 'todo-mode-line-control
287 "Function that returns a mode line control for Todos buffers. 287 "Function that returns a mode line control for Todo buffers.
288The function expects one argument holding the name of the current 288The function expects one argument holding the name of the current
289Todos category. The resulting control becomes the local value of 289Todo category. The resulting control becomes the local value of
290`mode-line-buffer-identification' in each Todos buffer." 290`mode-line-buffer-identification' in each Todo buffer."
291 :type 'function 291 :type 'function
292 :group 'todos-display) 292 :group 'todo-display)
293 293
294(defcustom todos-highlight-item nil 294(defcustom todo-highlight-item nil
295 "Non-nil means highlight items at point." 295 "Non-nil means highlight items at point."
296 :type 'boolean 296 :type 'boolean
297 :initialize 'custom-initialize-default 297 :initialize 'custom-initialize-default
298 :set 'todos-reset-highlight-item 298 :set 'todo-reset-highlight-item
299 :group 'todos-display) 299 :group 'todo-display)
300 300
301(defcustom todos-wrap-lines t 301(defcustom todo-wrap-lines t
302 "Non-nil to activate Visual Line mode and use wrap prefix." 302 "Non-nil to activate Visual Line mode and use wrap prefix."
303 :type 'boolean 303 :type 'boolean
304 :group 'todos-display) 304 :group 'todo-display)
305 305
306(defcustom todos-indent-to-here 3 306(defcustom todo-indent-to-here 3
307 "Number of spaces to indent continuation lines of items. 307 "Number of spaces to indent continuation lines of items.
308This must be a positive number to ensure such items are fully 308This must be a positive number to ensure such items are fully
309shown in the Fancy Diary display." 309shown in the Fancy Diary display."
@@ -313,22 +313,22 @@ shown in the Fancy Diary display."
313 (widget-put widget :error 313 (widget-put widget :error
314 "Invalid value: must be a positive integer") 314 "Invalid value: must be a positive integer")
315 widget))) 315 widget)))
316 :group 'todos-display) 316 :group 'todo-display)
317 317
318(defun todos-indent () 318(defun todo-indent ()
319 "Indent from point to `todos-indent-to-here'." 319 "Indent from point to `todo-indent-to-here'."
320 (indent-to todos-indent-to-here todos-indent-to-here)) 320 (indent-to todo-indent-to-here todo-indent-to-here))
321 321
322(defcustom todos-show-with-done nil 322(defcustom todo-show-with-done nil
323 "Non-nil to display done items in all categories." 323 "Non-nil to display done items in all categories."
324 :type 'boolean 324 :type 'boolean
325 :group 'todos-display) 325 :group 'todo-display)
326 326
327;; ----------------------------------------------------------------------------- 327;; -----------------------------------------------------------------------------
328;;; Faces 328;;; Faces
329;; ----------------------------------------------------------------------------- 329;; -----------------------------------------------------------------------------
330 330
331(defface todos-mark 331(defface todo-mark
332 ;; '((t :inherit font-lock-warning-face)) 332 ;; '((t :inherit font-lock-warning-face))
333 '((((class color) 333 '((((class color)
334 (min-colors 88) 334 (min-colors 88)
@@ -352,9 +352,9 @@ shown in the Fancy Diary display."
352 (t 352 (t
353 (:weight bold :inverse-video t))) 353 (:weight bold :inverse-video t)))
354 "Face for marks on marked items." 354 "Face for marks on marked items."
355 :group 'todos-faces) 355 :group 'todo-faces)
356 356
357(defface todos-prefix-string 357(defface todo-prefix-string
358 ;; '((t :inherit font-lock-constant-face)) 358 ;; '((t :inherit font-lock-constant-face))
359 '((((class grayscale) (background light)) 359 '((((class grayscale) (background light))
360 (:foreground "LightGray" :weight bold :underline t)) 360 (:foreground "LightGray" :weight bold :underline t))
@@ -366,10 +366,10 @@ shown in the Fancy Diary display."
366 (((class color) (min-colors 16) (background dark)) (:foreground "Aquamarine")) 366 (((class color) (min-colors 16) (background dark)) (:foreground "Aquamarine"))
367 (((class color) (min-colors 8)) (:foreground "magenta")) 367 (((class color) (min-colors 8)) (:foreground "magenta"))
368 (t (:weight bold :underline t))) 368 (t (:weight bold :underline t)))
369 "Face for Todos prefix or numerical priority string." 369 "Face for Todo prefix or numerical priority string."
370 :group 'todos-faces) 370 :group 'todo-faces)
371 371
372(defface todos-top-priority 372(defface todo-top-priority
373 ;; bold font-lock-comment-face 373 ;; bold font-lock-comment-face
374 '((default :weight bold) 374 '((default :weight bold)
375 (((class grayscale) (background light)) :foreground "DimGray" :slant italic) 375 (((class grayscale) (background light)) :foreground "DimGray" :slant italic)
@@ -381,12 +381,12 @@ shown in the Fancy Diary display."
381 (((class color) (min-colors 8) (background light)) :foreground "red") 381 (((class color) (min-colors 8) (background light)) :foreground "red")
382 (((class color) (min-colors 8) (background dark)) :foreground "yellow") 382 (((class color) (min-colors 8) (background dark)) :foreground "yellow")
383 (t :slant italic)) 383 (t :slant italic))
384 "Face for top priority Todos item numerical priority string. 384 "Face for top priority Todo item numerical priority string.
385The item's priority number string has this face if the number is 385The item's priority number string has this face if the number is
386less than or equal the category's top priority setting." 386less than or equal the category's top priority setting."
387 :group 'todos-faces) 387 :group 'todo-faces)
388 388
389(defface todos-nondiary 389(defface todo-nondiary
390 ;; '((t :inherit font-lock-type-face)) 390 ;; '((t :inherit font-lock-type-face))
391 '((((class grayscale) (background light)) :foreground "Gray90" :weight bold) 391 '((((class grayscale) (background light)) :foreground "Gray90" :weight bold)
392 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold) 392 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
@@ -397,20 +397,20 @@ less than or equal the category's top priority setting."
397 (((class color) (min-colors 8)) :foreground "green") 397 (((class color) (min-colors 8)) :foreground "green")
398 (t :weight bold :underline t)) 398 (t :weight bold :underline t))
399 "Face for non-diary markers around todo item date/time header." 399 "Face for non-diary markers around todo item date/time header."
400 :group 'todos-faces) 400 :group 'todo-faces)
401 401
402(defface todos-date 402(defface todo-date
403 '((t :inherit diary)) 403 '((t :inherit diary))
404 "Face for the date string of a Todos item." 404 "Face for the date string of a Todo item."
405 :group 'todos-faces) 405 :group 'todo-faces)
406 406
407(defface todos-time 407(defface todo-time
408 '((t :inherit diary-time)) 408 '((t :inherit diary-time))
409 "Face for the time string of a Todos item." 409 "Face for the time string of a Todo item."
410 :group 'todos-faces) 410 :group 'todo-faces)
411 411
412(defface todos-diary-expired 412(defface todo-diary-expired
413 ;; Doesn't contrast enough with todos-date (= diary) face. 413 ;; Doesn't contrast enough with todo-date (= diary) face.
414 ;; ;; '((t :inherit warning)) 414 ;; ;; '((t :inherit warning))
415 ;; '((default :weight bold) 415 ;; '((default :weight bold)
416 ;; (((class color) (min-colors 16)) :foreground "DarkOrange") 416 ;; (((class color) (min-colors 16)) :foreground "DarkOrange")
@@ -424,9 +424,9 @@ less than or equal the category's top priority setting."
424 (((class color) (min-colors 8)) :foreground "blue") 424 (((class color) (min-colors 8)) :foreground "blue")
425 (t :inverse-video t)) 425 (t :inverse-video t))
426 "Face for expired dates of diary items." 426 "Face for expired dates of diary items."
427 :group 'todos-faces) 427 :group 'todo-faces)
428 428
429(defface todos-done-sep 429(defface todo-done-sep
430 ;; '((t :inherit font-lock-builtin-face)) 430 ;; '((t :inherit font-lock-builtin-face))
431 '((((class grayscale) (background light)) :foreground "LightGray" :weight bold) 431 '((((class grayscale) (background light)) :foreground "LightGray" :weight bold)
432 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold) 432 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
@@ -436,10 +436,10 @@ less than or equal the category's top priority setting."
436 (((class color) (min-colors 16) (background dark)) :foreground "LightSteelBlue") 436 (((class color) (min-colors 16) (background dark)) :foreground "LightSteelBlue")
437 (((class color) (min-colors 8)) :foreground "blue" :weight bold) 437 (((class color) (min-colors 8)) :foreground "blue" :weight bold)
438 (t :weight bold)) 438 (t :weight bold))
439 "Face for separator string bewteen done and not done Todos items." 439 "Face for separator string bewteen done and not done Todo items."
440 :group 'todos-faces) 440 :group 'todo-faces)
441 441
442(defface todos-done 442(defface todo-done
443 ;; '((t :inherit font-lock-keyword-face)) 443 ;; '((t :inherit font-lock-keyword-face))
444 '((((class grayscale) (background light)) :foreground "LightGray" :weight bold) 444 '((((class grayscale) (background light)) :foreground "LightGray" :weight bold)
445 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold) 445 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
@@ -449,10 +449,10 @@ less than or equal the category's top priority setting."
449 (((class color) (min-colors 16) (background dark)) :foreground "Cyan") 449 (((class color) (min-colors 16) (background dark)) :foreground "Cyan")
450 (((class color) (min-colors 8)) :foreground "cyan" :weight bold) 450 (((class color) (min-colors 8)) :foreground "cyan" :weight bold)
451 (t :weight bold)) 451 (t :weight bold))
452 "Face for done Todos item header string." 452 "Face for done Todo item header string."
453 :group 'todos-faces) 453 :group 'todo-faces)
454 454
455(defface todos-comment 455(defface todo-comment
456 ;; '((t :inherit font-lock-comment-face)) 456 ;; '((t :inherit font-lock-comment-face))
457 '((((class grayscale) (background light)) 457 '((((class grayscale) (background light))
458 :foreground "DimGray" :weight bold :slant italic) 458 :foreground "DimGray" :weight bold :slant italic)
@@ -471,10 +471,10 @@ less than or equal the category's top priority setting."
471 (((class color) (min-colors 8) (background dark)) 471 (((class color) (min-colors 8) (background dark))
472 :foreground "yellow") 472 :foreground "yellow")
473 (t :weight bold :slant italic)) 473 (t :weight bold :slant italic))
474 "Face for comments appended to done Todos items." 474 "Face for comments appended to done Todo items."
475 :group 'todos-faces) 475 :group 'todo-faces)
476 476
477(defface todos-search 477(defface todo-search
478 ;; '((t :inherit match)) 478 ;; '((t :inherit match))
479 '((((class color) 479 '((((class color)
480 (min-colors 88) 480 (min-colors 88)
@@ -497,10 +497,10 @@ less than or equal the category's top priority setting."
497 (:inverse-video t)) 497 (:inverse-video t))
498 (t 498 (t
499 (:background "gray"))) 499 (:background "gray")))
500 "Face for matches found by `todos-search'." 500 "Face for matches found by `todo-search'."
501 :group 'todos-faces) 501 :group 'todo-faces)
502 502
503(defface todos-button 503(defface todo-button
504 ;; '((t :inherit widget-field)) 504 ;; '((t :inherit widget-field))
505 '((((type tty)) 505 '((((type tty))
506 (:foreground "black" :background "yellow3")) 506 (:foreground "black" :background "yellow3"))
@@ -513,9 +513,9 @@ less than or equal the category's top priority setting."
513 (t 513 (t
514 (:slant italic))) 514 (:slant italic)))
515 "Face for buttons in table of categories." 515 "Face for buttons in table of categories."
516 :group 'todos-faces) 516 :group 'todo-faces)
517 517
518(defface todos-sorted-column 518(defface todo-sorted-column
519 '((((type tty)) 519 '((((type tty))
520 (:inverse-video t)) 520 (:inverse-video t))
521 (((class color) 521 (((class color)
@@ -527,9 +527,9 @@ less than or equal the category's top priority setting."
527 (t 527 (t
528 (:background "gray"))) 528 (:background "gray")))
529 "Face for sorted column in table of categories." 529 "Face for sorted column in table of categories."
530 :group 'todos-faces) 530 :group 'todo-faces)
531 531
532(defface todos-archived-only 532(defface todo-archived-only
533 ;; '((t (:inherit (shadow)))) 533 ;; '((t (:inherit (shadow))))
534 '((((class color) 534 '((((class color)
535 (background light)) 535 (background light))
@@ -540,9 +540,9 @@ less than or equal the category's top priority setting."
540 (t 540 (t
541 (:foreground "gray"))) 541 (:foreground "gray")))
542 "Face for archived-only category names in table of categories." 542 "Face for archived-only category names in table of categories."
543 :group 'todos-faces) 543 :group 'todo-faces)
544 544
545(defface todos-category-string 545(defface todo-category-string
546 ;; '((t :inherit font-lock-type-face)) 546 ;; '((t :inherit font-lock-type-face))
547 '((((class grayscale) (background light)) :foreground "Gray90" :weight bold) 547 '((((class grayscale) (background light)) :foreground "Gray90" :weight bold)
548 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold) 548 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
@@ -552,99 +552,99 @@ less than or equal the category's top priority setting."
552 (((class color) (min-colors 16) (background dark)) :foreground "PaleGreen") 552 (((class color) (min-colors 16) (background dark)) :foreground "PaleGreen")
553 (((class color) (min-colors 8)) :foreground "green") 553 (((class color) (min-colors 8)) :foreground "green")
554 (t :weight bold :underline t)) 554 (t :weight bold :underline t))
555 "Face for category-file header in Todos Filtered Items mode." 555 "Face for category-file header in Todo Filtered Items mode."
556 :group 'todos-faces) 556 :group 'todo-faces)
557 557
558;; ----------------------------------------------------------------------------- 558;; -----------------------------------------------------------------------------
559;;; Entering and exiting Todos 559;;; Entering and exiting Todo
560;; ----------------------------------------------------------------------------- 560;; -----------------------------------------------------------------------------
561 561
562(defcustom todos-visit-files-commands (list 'find-file 'dired-find-file) 562(defcustom todo-visit-files-commands (list 'find-file 'dired-find-file)
563 "List of file finding commands for `todos-display-as-todos-file'. 563 "List of file finding commands for `todo-display-as-todo-file'.
564Invoking these commands to visit a Todos or Todos Archive file 564Invoking these commands to visit a Todo or Todo Archive file
565calls `todos-show' or `todos-find-archive', so that the file is 565calls `todo-show' or `todo-find-archive', so that the file is
566displayed correctly." 566displayed correctly."
567 :type '(repeat function) 567 :type '(repeat function)
568 :group 'todos) 568 :group 'todo)
569 569
570(defun todos-short-file-name (file) 570(defun todo-short-file-name (file)
571 "Return short form of Todos FILE. 571 "Return short form of Todo FILE.
572This lacks the extension and directory components." 572This lacks the extension and directory components."
573 (when (stringp file) 573 (when (stringp file)
574 (file-name-sans-extension (file-name-nondirectory file)))) 574 (file-name-sans-extension (file-name-nondirectory file))))
575 575
576(defcustom todos-default-todos-file (todos-short-file-name 576(defcustom todo-default-todo-file (todo-short-file-name
577 (car (funcall todos-files-function))) 577 (car (funcall todo-files-function)))
578 "Todos file visited by first session invocation of `todos-show'." 578 "Todo file visited by first session invocation of `todo-show'."
579 :type `(radio ,@(mapcar (lambda (f) (list 'const f)) 579 :type `(radio ,@(mapcar (lambda (f) (list 'const f))
580 (mapcar 'todos-short-file-name 580 (mapcar 'todo-short-file-name
581 (funcall todos-files-function)))) 581 (funcall todo-files-function))))
582 :group 'todos) 582 :group 'todo)
583 583
584(defcustom todos-show-current-file t 584(defcustom todo-show-current-file t
585 "Non-nil to make `todos-show' visit the current Todos file. 585 "Non-nil to make `todo-show' visit the current Todo file.
586Otherwise, `todos-show' always visits `todos-default-todos-file'." 586Otherwise, `todo-show' always visits `todo-default-todo-file'."
587 :type 'boolean 587 :type 'boolean
588 :initialize 'custom-initialize-default 588 :initialize 'custom-initialize-default
589 :set 'todos-set-show-current-file 589 :set 'todo-set-show-current-file
590 :group 'todos) 590 :group 'todo)
591 591
592(defcustom todos-show-first 'first 592(defcustom todo-show-first 'first
593 "What action to take on first use of `todos-show' on a file." 593 "What action to take on first use of `todo-show' on a file."
594 :type '(choice (const :tag "Show first category" first) 594 :type '(choice (const :tag "Show first category" first)
595 (const :tag "Show table of categories" table) 595 (const :tag "Show table of categories" table)
596 (const :tag "Show top priorities" top) 596 (const :tag "Show top priorities" top)
597 (const :tag "Show diary items" diary) 597 (const :tag "Show diary items" diary)
598 (const :tag "Show regexp items" regexp)) 598 (const :tag "Show regexp items" regexp))
599 :group 'todos) 599 :group 'todo)
600 600
601(defcustom todos-add-item-if-new-category t 601(defcustom todo-add-item-if-new-category t
602 "Non-nil to prompt for an item after adding a new category." 602 "Non-nil to prompt for an item after adding a new category."
603 :type 'boolean 603 :type 'boolean
604 :group 'todos-edit) 604 :group 'todo-edit)
605 605
606(defcustom todos-initial-file "Todo" 606(defcustom todo-initial-file "Todo"
607 "Default file name offered on adding first Todos file." 607 "Default file name offered on adding first Todo file."
608 :type 'string 608 :type 'string
609 :group 'todos) 609 :group 'todo)
610 610
611(defcustom todos-initial-category "Todo" 611(defcustom todo-initial-category "Todo"
612 "Default category name offered on initializing a new Todos file." 612 "Default category name offered on initializing a new Todo file."
613 :type 'string 613 :type 'string
614 :group 'todos) 614 :group 'todo)
615 615
616(defcustom todos-category-completions-files nil 616(defcustom todo-category-completions-files nil
617 "List of files for building `todos-read-category' completions." 617 "List of files for building `todo-read-category' completions."
618 :type `(set ,@(mapcar (lambda (f) (list 'const f)) 618 :type `(set ,@(mapcar (lambda (f) (list 'const f))
619 (mapcar 'todos-short-file-name 619 (mapcar 'todo-short-file-name
620 (funcall todos-files-function)))) 620 (funcall todo-files-function))))
621 :group 'todos) 621 :group 'todo)
622 622
623(defcustom todos-completion-ignore-case nil 623(defcustom todo-completion-ignore-case nil
624 "Non-nil means case is ignored by `todos-read-*' functions." 624 "Non-nil means case is ignored by `todo-read-*' functions."
625 :type 'boolean 625 :type 'boolean
626 :group 'todos) 626 :group 'todo)
627 627
628(defun todos-show (&optional solicit-file) 628(defun todo-show (&optional solicit-file)
629 "Visit a Todos file and display one of its categories. 629 "Visit a Todo file and display one of its categories.
630 630
631When invoked in Todos mode, prompt for which todo file to visit. 631When invoked in Todo mode, prompt for which todo file to visit.
632When invoked outside of Todos mode with non-nil prefix argument 632When invoked outside of Todo mode with non-nil prefix argument
633SOLICIT-FILE prompt for which todo file to visit; otherwise visit 633SOLICIT-FILE prompt for which todo file to visit; otherwise visit
634`todos-default-todos-file'. Subsequent invocations from outside 634`todo-default-todo-file'. Subsequent invocations from outside
635of Todos mode revisit this file or, with option 635of Todo mode revisit this file or, with option
636`todos-show-current-file' non-nil (the default), whichever Todos 636`todo-show-current-file' non-nil (the default), whichever Todo
637file was last visited. 637file was last visited.
638 638
639Calling this command before any Todos file exists prompts for a 639Calling this command before any Todo file exists prompts for a
640file name and an initial category (defaulting to 640file name and an initial category (defaulting to
641`todos-initial-file' and `todos-initial-category'), creates both 641`todo-initial-file' and `todo-initial-category'), creates both
642of these, visits the file and displays the category, and if 642of these, visits the file and displays the category, and if
643option `todos-add-item-if-new-category' is non-nil (the default), 643option `todo-add-item-if-new-category' is non-nil (the default),
644prompts for the first item. 644prompts for the first item.
645 645
646The first invocation of this command on an existing Todos file 646The first invocation of this command on an existing Todo file
647interacts with the option `todos-show-first': if its value is 647interacts with the option `todo-show-first': if its value is
648`first' (the default), show the first category in the file; if 648`first' (the default), show the first category in the file; if
649its value is `table', show the table of categories in the file; 649its value is `table', show the table of categories in the file;
650if its value is one of `top', `diary' or `regexp', show the 650if its value is one of `top', `diary' or `regexp', show the
@@ -652,62 +652,62 @@ corresponding saved top priorities, diary items, or regexp items
652file, if any. Subsequent invocations always show the file's 652file, if any. Subsequent invocations always show the file's
653current (i.e., last displayed) category. 653current (i.e., last displayed) category.
654 654
655In Todos mode just the category's unfinished todo items are shown 655In Todo mode just the category's unfinished todo items are shown
656by default. The done items are hidden, but typing 656by default. The done items are hidden, but typing
657`\\[todos-toggle-view-done-items]' displays them below the todo 657`\\[todo-toggle-view-done-items]' displays them below the todo
658items. With non-nil user option `todos-show-with-done' both todo 658items. With non-nil user option `todo-show-with-done' both todo
659and done items are always shown on visiting a category. 659and done items are always shown on visiting a category.
660 660
661Invoking this command in Todos Archive mode visits the 661Invoking this command in Todo Archive mode visits the
662corresponding Todos file, displaying the corresponding category." 662corresponding Todo file, displaying the corresponding category."
663 (interactive "P") 663 (interactive "P")
664 (let* ((cat) 664 (let* ((cat)
665 (show-first todos-show-first) 665 (show-first todo-show-first)
666 (file (cond ((or solicit-file 666 (file (cond ((or solicit-file
667 (and (called-interactively-p 'any) 667 (and (called-interactively-p 'any)
668 (memq major-mode '(todos-mode 668 (memq major-mode '(todo-mode
669 todos-archive-mode 669 todo-archive-mode
670 todos-filtered-items-mode)))) 670 todo-filtered-items-mode))))
671 (if (funcall todos-files-function) 671 (if (funcall todo-files-function)
672 (todos-read-file-name "Choose a Todos file to visit: " 672 (todo-read-file-name "Choose a Todo file to visit: "
673 nil t) 673 nil t)
674 (user-error "There are no Todos files"))) 674 (user-error "There are no Todo files")))
675 ((and (eq major-mode 'todos-archive-mode) 675 ((and (eq major-mode 'todo-archive-mode)
676 ;; Called noninteractively via todos-quit 676 ;; Called noninteractively via todo-quit
677 ;; to jump to corresponding category in 677 ;; to jump to corresponding category in
678 ;; todo file. 678 ;; todo file.
679 (not (called-interactively-p 'any))) 679 (not (called-interactively-p 'any)))
680 (setq cat (todos-current-category)) 680 (setq cat (todo-current-category))
681 (concat (file-name-sans-extension 681 (concat (file-name-sans-extension
682 todos-current-todos-file) ".todo")) 682 todo-current-todo-file) ".todo"))
683 (t 683 (t
684 (or todos-current-todos-file 684 (or todo-current-todo-file
685 (and todos-show-current-file 685 (and todo-show-current-file
686 todos-global-current-todos-file) 686 todo-global-current-todo-file)
687 (todos-absolute-file-name todos-default-todos-file) 687 (todo-absolute-file-name todo-default-todo-file)
688 (todos-add-file))))) 688 (todo-add-file)))))
689 add-item first-file) 689 add-item first-file)
690 (unless todos-default-todos-file 690 (unless todo-default-todo-file
691 ;; We just initialized the first todo file, so make it the default. 691 ;; We just initialized the first todo file, so make it the default.
692 (setq todos-default-todos-file (todos-short-file-name file) 692 (setq todo-default-todo-file (todo-short-file-name file)
693 first-file t) 693 first-file t)
694 (todos-reevaluate-default-file-defcustom)) 694 (todo-reevaluate-default-file-defcustom))
695 (unless (member file todos-visited) 695 (unless (member file todo-visited)
696 ;; Can't setq t-c-t-f here, otherwise wrong file shown when 696 ;; Can't setq t-c-t-f here, otherwise wrong file shown when
697 ;; todos-show is called from todos-show-categories-table. 697 ;; todo-show is called from todo-show-categories-table.
698 (let ((todos-current-todos-file file)) 698 (let ((todo-current-todo-file file))
699 (cond ((eq todos-show-first 'table) 699 (cond ((eq todo-show-first 'table)
700 (todos-show-categories-table)) 700 (todo-show-categories-table))
701 ((memq todos-show-first '(top diary regexp)) 701 ((memq todo-show-first '(top diary regexp))
702 (let* ((shortf (todos-short-file-name file)) 702 (let* ((shortf (todo-short-file-name file))
703 (fi-file (todos-absolute-file-name 703 (fi-file (todo-absolute-file-name
704 shortf todos-show-first))) 704 shortf todo-show-first)))
705 (when (eq todos-show-first 'regexp) 705 (when (eq todo-show-first 'regexp)
706 (let ((rxfiles (directory-files todos-directory t 706 (let ((rxfiles (directory-files todo-directory t
707 ".*\\.todr$" t))) 707 ".*\\.todr$" t)))
708 (when (and rxfiles (> (length rxfiles) 1)) 708 (when (and rxfiles (> (length rxfiles) 1))
709 (let ((rxf (mapcar 'todos-short-file-name rxfiles))) 709 (let ((rxf (mapcar 'todo-short-file-name rxfiles)))
710 (setq fi-file (todos-absolute-file-name 710 (setq fi-file (todo-absolute-file-name
711 (completing-read 711 (completing-read
712 "Choose a regexp items file: " 712 "Choose a regexp items file: "
713 rxf) 'regexp)))))) 713 rxf) 'regexp))))))
@@ -716,29 +716,29 @@ corresponding Todos file, displaying the corresponding category."
716 (selected-window) 716 (selected-window)
717 (set-buffer (find-file-noselect fi-file 'nowarn))) 717 (set-buffer (find-file-noselect fi-file 'nowarn)))
718 (message "There is no %s file for %s" 718 (message "There is no %s file for %s"
719 (cond ((eq todos-show-first 'top) 719 (cond ((eq todo-show-first 'top)
720 "top priorities") 720 "top priorities")
721 ((eq todos-show-first 'diary) 721 ((eq todo-show-first 'diary)
722 "diary items") 722 "diary items")
723 ((eq todos-show-first 'regexp) 723 ((eq todo-show-first 'regexp)
724 "regexp items")) 724 "regexp items"))
725 shortf) 725 shortf)
726 (setq todos-show-first 'first))))))) 726 (setq todo-show-first 'first)))))))
727 (when (or (member file todos-visited) 727 (when (or (member file todo-visited)
728 (eq todos-show-first 'first)) 728 (eq todo-show-first 'first))
729 (set-window-buffer (selected-window) 729 (set-window-buffer (selected-window)
730 (set-buffer (find-file-noselect file 'nowarn))) 730 (set-buffer (find-file-noselect file 'nowarn)))
731 ;; When quitting archive file, show corresponding category in 731 ;; When quitting archive file, show corresponding category in
732 ;; Todos file, if it exists. 732 ;; Todo file, if it exists.
733 (when (assoc cat todos-categories) 733 (when (assoc cat todo-categories)
734 (setq todos-category-number (todos-category-number cat))) 734 (setq todo-category-number (todo-category-number cat)))
735 ;; If this is a new Todos file, add its first category. 735 ;; If this is a new Todo file, add its first category.
736 (when (zerop (buffer-size)) 736 (when (zerop (buffer-size))
737 (let (cat-added) 737 (let (cat-added)
738 (unwind-protect 738 (unwind-protect
739 (setq todos-category-number 739 (setq todo-category-number
740 (todos-add-category todos-current-todos-file "") 740 (todo-add-category todo-current-todo-file "")
741 add-item todos-add-item-if-new-category 741 add-item todo-add-item-if-new-category
742 cat-added t) 742 cat-added t)
743 (if cat-added 743 (if cat-added
744 ;; If the category was added, save the file now, so we 744 ;; If the category was added, save the file now, so we
@@ -749,57 +749,57 @@ corresponding Todos file, displaying the corresponding category."
749 ;; If user cancels before adding the category, clean up 749 ;; If user cancels before adding the category, clean up
750 ;; and exit, so we have a fresh slate the next time. 750 ;; and exit, so we have a fresh slate the next time.
751 (delete-file file) 751 (delete-file file)
752 (setq todos-files (delete file todos-files)) 752 (setq todo-files (delete file todo-files))
753 (when first-file 753 (when first-file
754 (setq todos-default-todos-file nil 754 (setq todo-default-todo-file nil
755 todos-current-todos-file nil)) 755 todo-current-todo-file nil))
756 (kill-buffer) 756 (kill-buffer)
757 (keyboard-quit))))) 757 (keyboard-quit)))))
758 (save-excursion (todos-category-select)) 758 (save-excursion (todo-category-select))
759 (when add-item (todos-basic-insert-item))) 759 (when add-item (todo-basic-insert-item)))
760 (setq todos-show-first show-first) 760 (setq todo-show-first show-first)
761 (add-to-list 'todos-visited file))) 761 (add-to-list 'todo-visited file)))
762 762
763(defun todos-save () 763(defun todo-save ()
764 "Save the current Todos file." 764 "Save the current Todo file."
765 (interactive) 765 (interactive)
766 (cond ((eq major-mode 'todos-filtered-items-mode) 766 (cond ((eq major-mode 'todo-filtered-items-mode)
767 (todos-check-filtered-items-file) 767 (todo-check-filtered-items-file)
768 (todos-save-filtered-items-buffer)) 768 (todo-save-filtered-items-buffer))
769 (t 769 (t
770 (save-buffer)))) 770 (save-buffer))))
771 771
772(defvar todos-descending-counts) 772(defvar todo-descending-counts)
773 773
774(defun todos-quit () 774(defun todo-quit ()
775 "Exit the current Todos-related buffer. 775 "Exit the current Todo-related buffer.
776Depending on the specific mode, this either kills the buffer or 776Depending on the specific mode, this either kills the buffer or
777buries it and restores state as needed." 777buries it and restores state as needed."
778 (interactive) 778 (interactive)
779 (let ((buf (current-buffer))) 779 (let ((buf (current-buffer)))
780 (cond ((eq major-mode 'todos-categories-mode) 780 (cond ((eq major-mode 'todo-categories-mode)
781 ;; Postpone killing buffer till after calling todos-show, to 781 ;; Postpone killing buffer till after calling todo-show, to
782 ;; prevent killing todos-mode buffer. 782 ;; prevent killing todo-mode buffer.
783 (setq todos-descending-counts nil) 783 (setq todo-descending-counts nil)
784 ;; Ensure todos-show calls todos-show-categories-table only on 784 ;; Ensure todo-show calls todo-show-categories-table only on
785 ;; first invocation per file. 785 ;; first invocation per file.
786 (when (eq todos-show-first 'table) 786 (when (eq todo-show-first 'table)
787 (add-to-list 'todos-visited todos-current-todos-file)) 787 (add-to-list 'todo-visited todo-current-todo-file))
788 (todos-show) 788 (todo-show)
789 (kill-buffer buf)) 789 (kill-buffer buf))
790 ((eq major-mode 'todos-filtered-items-mode) 790 ((eq major-mode 'todo-filtered-items-mode)
791 (kill-buffer) 791 (kill-buffer)
792 (unless (eq major-mode 'todos-mode) (todos-show))) 792 (unless (eq major-mode 'todo-mode) (todo-show)))
793 ((eq major-mode 'todos-archive-mode) 793 ((eq major-mode 'todo-archive-mode)
794 ;; Have to write a newly created archive to file to avoid 794 ;; Have to write a newly created archive to file to avoid
795 ;; subsequent errors. 795 ;; subsequent errors.
796 (todos-save) 796 (todo-save)
797 (todos-show) 797 (todo-show)
798 (bury-buffer buf)) 798 (bury-buffer buf))
799 ((eq major-mode 'todos-mode) 799 ((eq major-mode 'todo-mode)
800 (todos-save) 800 (todo-save)
801 ;; If we just quit archive mode, just burying the buffer 801 ;; If we just quit archive mode, just burying the buffer
802 ;; in todos-mode would return to archive. 802 ;; in todo-mode would return to archive.
803 (set-window-buffer (selected-window) 803 (set-window-buffer (selected-window)
804 (set-buffer (other-buffer))) 804 (set-buffer (other-buffer)))
805 (bury-buffer buf))))) 805 (bury-buffer buf)))))
@@ -808,110 +808,110 @@ buries it and restores state as needed."
808;;; Navigation between and within categories 808;;; Navigation between and within categories
809;; ----------------------------------------------------------------------------- 809;; -----------------------------------------------------------------------------
810 810
811(defcustom todos-skip-archived-categories nil 811(defcustom todo-skip-archived-categories nil
812 "Non-nil to handle categories with only archived items specially. 812 "Non-nil to handle categories with only archived items specially.
813 813
814Sequential category navigation using \\[todos-forward-category] 814Sequential category navigation using \\[todo-forward-category]
815or \\[todos-backward-category] skips categories that contain only 815or \\[todo-backward-category] skips categories that contain only
816archived items. Other commands still recognize these categories. 816archived items. Other commands still recognize these categories.
817In Todos Categories mode (\\[todos-show-categories-table]) these 817In Todo Categories mode (\\[todo-show-categories-table]) these
818categories shown in `todos-archived-only' face and pressing the 818categories shown in `todo-archived-only' face and pressing the
819category button visits the category in the archive instead of the 819category button visits the category in the archive instead of the
820todo file." 820todo file."
821 :type 'boolean 821 :type 'boolean
822 :group 'todos-display) 822 :group 'todo-display)
823 823
824(defun todos-forward-category (&optional back) 824(defun todo-forward-category (&optional back)
825 "Visit the numerically next category in this Todos file. 825 "Visit the numerically next category in this Todo file.
826If the current category is the highest numbered, visit the first 826If the current category is the highest numbered, visit the first
827category. With non-nil argument BACK, visit the numerically 827category. With non-nil argument BACK, visit the numerically
828previous category (the highest numbered one, if the current 828previous category (the highest numbered one, if the current
829category is the first)." 829category is the first)."
830 (interactive) 830 (interactive)
831 (setq todos-category-number 831 (setq todo-category-number
832 (1+ (mod (- todos-category-number (if back 2 0)) 832 (1+ (mod (- todo-category-number (if back 2 0))
833 (length todos-categories)))) 833 (length todo-categories))))
834 (when todos-skip-archived-categories 834 (when todo-skip-archived-categories
835 (while (and (zerop (todos-get-count 'todo)) 835 (while (and (zerop (todo-get-count 'todo))
836 (zerop (todos-get-count 'done)) 836 (zerop (todo-get-count 'done))
837 (not (zerop (todos-get-count 'archived)))) 837 (not (zerop (todo-get-count 'archived))))
838 (setq todos-category-number 838 (setq todo-category-number
839 (apply (if back '1- '1+) (list todos-category-number))))) 839 (apply (if back '1- '1+) (list todo-category-number)))))
840 (todos-category-select) 840 (todo-category-select)
841 (goto-char (point-min))) 841 (goto-char (point-min)))
842 842
843(defun todos-backward-category () 843(defun todo-backward-category ()
844 "Visit the numerically previous category in this Todos file. 844 "Visit the numerically previous category in this Todo file.
845If the current category is the highest numbered, visit the first 845If the current category is the highest numbered, visit the first
846category." 846category."
847 (interactive) 847 (interactive)
848 (todos-forward-category t)) 848 (todo-forward-category t))
849 849
850(defvar todos-categories-buffer) 850(defvar todo-categories-buffer)
851 851
852(defun todos-jump-to-category (&optional file where) 852(defun todo-jump-to-category (&optional file where)
853 "Prompt for a category in a Todos file and jump to it. 853 "Prompt for a category in a Todo file and jump to it.
854 854
855With non-nil FILE (interactively a prefix argument), prompt for a 855With non-nil FILE (interactively a prefix argument), prompt for a
856specific Todos file and choose (with TAB completion) a category 856specific Todo file and choose (with TAB completion) a category
857in it to jump to; otherwise, choose and jump to any category in 857in it to jump to; otherwise, choose and jump to any category in
858either the current Todos file or a file in 858either the current Todo file or a file in
859`todos-category-completions-files'. 859`todo-category-completions-files'.
860 860
861Also accept a non-existing category name and ask whether to add a 861Also accept a non-existing category name and ask whether to add a
862new category by that name; on confirmation, add it and jump to 862new category by that name; on confirmation, add it and jump to
863that category, and if option `todos-add-item-if-new-category' is 863that category, and if option `todo-add-item-if-new-category' is
864non-nil (the default), then prompt for the first item. 864non-nil (the default), then prompt for the first item.
865 865
866In noninteractive calls non-nil WHERE specifies either the goal 866In noninteractive calls non-nil WHERE specifies either the goal
867category or its file. If its value is `archive', the choice of 867category or its file. If its value is `archive', the choice of
868categories is restricted to the current archive file or the 868categories is restricted to the current archive file or the
869archive you were prompted to choose; this is used by 869archive you were prompted to choose; this is used by
870`todos-jump-to-archive-category'. If its value is the name of a 870`todo-jump-to-archive-category'. If its value is the name of a
871category, jump directly to that category; this is used in Todos 871category, jump directly to that category; this is used in Todo
872Categories mode." 872Categories mode."
873 (interactive "P") 873 (interactive "P")
874 ;; If invoked outside of Todos mode and there is not yet any Todos 874 ;; If invoked outside of Todo mode and there is not yet any Todo
875 ;; file, initialize one. 875 ;; file, initialize one.
876 (if (null todos-files) 876 (if (null todo-files)
877 (todos-show) 877 (todo-show)
878 (let* ((archive (eq where 'archive)) 878 (let* ((archive (eq where 'archive))
879 (cat (unless archive where)) 879 (cat (unless archive where))
880 (file0 (when cat ; We're in Todos Categories mode. 880 (file0 (when cat ; We're in Todo Categories mode.
881 ;; With non-nil `todos-skip-archived-categories' 881 ;; With non-nil `todo-skip-archived-categories'
882 ;; jump to archive file of a category with only 882 ;; jump to archive file of a category with only
883 ;; archived items. 883 ;; archived items.
884 (if (and todos-skip-archived-categories 884 (if (and todo-skip-archived-categories
885 (zerop (todos-get-count 'todo cat)) 885 (zerop (todo-get-count 'todo cat))
886 (zerop (todos-get-count 'done cat)) 886 (zerop (todo-get-count 'done cat))
887 (not (zerop (todos-get-count 'archived cat)))) 887 (not (zerop (todo-get-count 'archived cat))))
888 (concat (file-name-sans-extension 888 (concat (file-name-sans-extension
889 todos-current-todos-file) ".toda") 889 todo-current-todo-file) ".toda")
890 ;; Otherwise, jump to current todos file. 890 ;; Otherwise, jump to current todo file.
891 todos-current-todos-file))) 891 todo-current-todo-file)))
892 (len (length todos-categories)) 892 (len (length todo-categories))
893 (cat+file (unless cat 893 (cat+file (unless cat
894 (todos-read-category "Jump to category: " 894 (todo-read-category "Jump to category: "
895 (if archive 'archive) file))) 895 (if archive 'archive) file)))
896 (add-item (and todos-add-item-if-new-category 896 (add-item (and todo-add-item-if-new-category
897 (> (length todos-categories) len))) 897 (> (length todo-categories) len)))
898 (category (or cat (car cat+file)))) 898 (category (or cat (car cat+file))))
899 (unless cat (setq file0 (cdr cat+file))) 899 (unless cat (setq file0 (cdr cat+file)))
900 (with-current-buffer (find-file-noselect file0 'nowarn) 900 (with-current-buffer (find-file-noselect file0 'nowarn)
901 (setq todos-current-todos-file file0) 901 (setq todo-current-todo-file file0)
902 ;; If called from Todos Categories mode, clean up before jumping. 902 ;; If called from Todo Categories mode, clean up before jumping.
903 (if (string= (buffer-name) todos-categories-buffer) 903 (if (string= (buffer-name) todo-categories-buffer)
904 (kill-buffer)) 904 (kill-buffer))
905 (set-window-buffer (selected-window) 905 (set-window-buffer (selected-window)
906 (set-buffer (find-buffer-visiting file0))) 906 (set-buffer (find-buffer-visiting file0)))
907 (unless todos-global-current-todos-file 907 (unless todo-global-current-todo-file
908 (setq todos-global-current-todos-file todos-current-todos-file)) 908 (setq todo-global-current-todo-file todo-current-todo-file))
909 (todos-category-number category) 909 (todo-category-number category)
910 (todos-category-select) 910 (todo-category-select)
911 (goto-char (point-min)) 911 (goto-char (point-min))
912 (when add-item (todos-basic-insert-item)))))) 912 (when add-item (todo-basic-insert-item))))))
913 913
914(defun todos-next-item (&optional count) 914(defun todo-next-item (&optional count)
915 "Move point down to the beginning of the next item. 915 "Move point down to the beginning of the next item.
916With positive numerical prefix COUNT, move point COUNT items 916With positive numerical prefix COUNT, move point COUNT items
917downward. 917downward.
@@ -931,11 +931,11 @@ the the empty line above the done items separator."
931 (cond ((and current-prefix-arg (< count 1)) 931 (cond ((and current-prefix-arg (< count 1))
932 (user-error "The prefix argument must be a positive number")) 932 (user-error "The prefix argument must be a positive number"))
933 (current-prefix-arg 933 (current-prefix-arg
934 (todos-forward-item count)) 934 (todo-forward-item count))
935 (t 935 (t
936 (todos-forward-item)))) 936 (todo-forward-item))))
937 937
938(defun todos-previous-item (&optional count) 938(defun todo-previous-item (&optional count)
939 "Move point up to start of item with next higher priority. 939 "Move point up to start of item with next higher priority.
940With positive numerical prefix COUNT, move point COUNT items 940With positive numerical prefix COUNT, move point COUNT items
941upward. 941upward.
@@ -954,67 +954,67 @@ empty line above the done items separator."
954 (cond ((and current-prefix-arg (< count 1)) 954 (cond ((and current-prefix-arg (< count 1))
955 (user-error "The prefix argument must be a positive number")) 955 (user-error "The prefix argument must be a positive number"))
956 (current-prefix-arg 956 (current-prefix-arg
957 (todos-backward-item count)) 957 (todo-backward-item count))
958 (t 958 (t
959 (todos-backward-item))))) 959 (todo-backward-item)))))
960 960
961;; ----------------------------------------------------------------------------- 961;; -----------------------------------------------------------------------------
962;;; Display toggle commands 962;;; Display toggle commands
963;; ----------------------------------------------------------------------------- 963;; -----------------------------------------------------------------------------
964 964
965(defun todos-toggle-prefix-numbers () 965(defun todo-toggle-prefix-numbers ()
966 "Hide item numbering if shown, show if hidden." 966 "Hide item numbering if shown, show if hidden."
967 (interactive) 967 (interactive)
968 (save-excursion 968 (save-excursion
969 (save-restriction 969 (save-restriction
970 (goto-char (point-min)) 970 (goto-char (point-min))
971 (let* ((ov (todos-get-overlay 'prefix)) 971 (let* ((ov (todo-get-overlay 'prefix))
972 (show-done (re-search-forward todos-done-string-start nil t)) 972 (show-done (re-search-forward todo-done-string-start nil t))
973 (todos-show-with-done show-done) 973 (todo-show-with-done show-done)
974 (todos-number-prefix (not (equal (overlay-get ov 'before-string) 974 (todo-number-prefix (not (equal (overlay-get ov 'before-string)
975 "1 ")))) 975 "1 "))))
976 (if (eq major-mode 'todos-filtered-items-mode) 976 (if (eq major-mode 'todo-filtered-items-mode)
977 (todos-prefix-overlays) 977 (todo-prefix-overlays)
978 (todos-category-select)))))) 978 (todo-category-select))))))
979 979
980(defun todos-toggle-view-done-items () 980(defun todo-toggle-view-done-items ()
981 "Show hidden or hide visible done items in current category." 981 "Show hidden or hide visible done items in current category."
982 (interactive) 982 (interactive)
983 (if (zerop (todos-get-count 'done (todos-current-category))) 983 (if (zerop (todo-get-count 'done (todo-current-category)))
984 (message "There are no done items in this category.") 984 (message "There are no done items in this category.")
985 (let ((opoint (point))) 985 (let ((opoint (point)))
986 (goto-char (point-min)) 986 (goto-char (point-min))
987 (let* ((shown (re-search-forward todos-done-string-start nil t)) 987 (let* ((shown (re-search-forward todo-done-string-start nil t))
988 (todos-show-with-done (not shown))) 988 (todo-show-with-done (not shown)))
989 (todos-category-select) 989 (todo-category-select)
990 (goto-char opoint) 990 (goto-char opoint)
991 ;; If start of done items sections is below the bottom of the 991 ;; If start of done items sections is below the bottom of the
992 ;; window, make it visible. 992 ;; window, make it visible.
993 (unless shown 993 (unless shown
994 (setq shown (progn 994 (setq shown (progn
995 (goto-char (point-min)) 995 (goto-char (point-min))
996 (re-search-forward todos-done-string-start nil t))) 996 (re-search-forward todo-done-string-start nil t)))
997 (if (not (pos-visible-in-window-p shown)) 997 (if (not (pos-visible-in-window-p shown))
998 (recenter) 998 (recenter)
999 (goto-char opoint))))))) 999 (goto-char opoint)))))))
1000 1000
1001(defun todos-toggle-view-done-only () 1001(defun todo-toggle-view-done-only ()
1002 "Switch between displaying only done or only todo items." 1002 "Switch between displaying only done or only todo items."
1003 (interactive) 1003 (interactive)
1004 (setq todos-show-done-only (not todos-show-done-only)) 1004 (setq todo-show-done-only (not todo-show-done-only))
1005 (todos-category-select)) 1005 (todo-category-select))
1006 1006
1007(defun todos-toggle-item-highlighting () 1007(defun todo-toggle-item-highlighting ()
1008 "Highlight or unhighlight the todo item the cursor is on." 1008 "Highlight or unhighlight the todo item the cursor is on."
1009 (interactive) 1009 (interactive)
1010 (eval-when-compile (require 'hl-line)) 1010 (eval-when-compile (require 'hl-line))
1011 (when (memq major-mode 1011 (when (memq major-mode
1012 '(todos-mode todos-archive-mode todos-filtered-items-mode)) 1012 '(todo-mode todo-archive-mode todo-filtered-items-mode))
1013 (if hl-line-mode 1013 (if hl-line-mode
1014 (hl-line-mode -1) 1014 (hl-line-mode -1)
1015 (hl-line-mode 1)))) 1015 (hl-line-mode 1))))
1016 1016
1017(defun todos-toggle-item-header () 1017(defun todo-toggle-item-header ()
1018 "Hide or show item date-time headers in the current file. 1018 "Hide or show item date-time headers in the current file.
1019With done items, this hides only the done date-time string, not 1019With done items, this hides only the done date-time string, not
1020the the original date-time string." 1020the the original date-time string."
@@ -1022,84 +1022,84 @@ the the original date-time string."
1022 (save-excursion 1022 (save-excursion
1023 (save-restriction 1023 (save-restriction
1024 (goto-char (point-min)) 1024 (goto-char (point-min))
1025 (let ((ov (todos-get-overlay 'header))) 1025 (let ((ov (todo-get-overlay 'header)))
1026 (if ov 1026 (if ov
1027 (remove-overlays 1 (1+ (buffer-size)) 'todos 'header) 1027 (remove-overlays 1 (1+ (buffer-size)) 'todo 'header)
1028 (widen) 1028 (widen)
1029 (goto-char (point-min)) 1029 (goto-char (point-min))
1030 (while (not (eobp)) 1030 (while (not (eobp))
1031 (when (re-search-forward 1031 (when (re-search-forward
1032 (concat todos-item-start 1032 (concat todo-item-start
1033 "\\( " diary-time-regexp "\\)?" 1033 "\\( " diary-time-regexp "\\)?"
1034 (regexp-quote todos-nondiary-end) "? ") 1034 (regexp-quote todo-nondiary-end) "? ")
1035 nil t) 1035 nil t)
1036 (setq ov (make-overlay (match-beginning 0) (match-end 0) nil t)) 1036 (setq ov (make-overlay (match-beginning 0) (match-end 0) nil t))
1037 (overlay-put ov 'todos 'header) 1037 (overlay-put ov 'todo 'header)
1038 (overlay-put ov 'display "")) 1038 (overlay-put ov 'display ""))
1039 (todos-forward-item))))))) 1039 (todo-forward-item)))))))
1040 1040
1041;; ----------------------------------------------------------------------------- 1041;; -----------------------------------------------------------------------------
1042;;; File and category editing 1042;;; File and category editing
1043;; ----------------------------------------------------------------------------- 1043;; -----------------------------------------------------------------------------
1044 1044
1045(defun todos-add-file () 1045(defun todo-add-file ()
1046 "Name and initialize a new Todos file. 1046 "Name and initialize a new Todo file.
1047Interactively, prompt for a category and display it, and if 1047Interactively, prompt for a category and display it, and if
1048option `todos-add-item-if-new-category' is non-nil (the default), 1048option `todo-add-item-if-new-category' is non-nil (the default),
1049prompt for the first item. 1049prompt for the first item.
1050Noninteractively, return the name of the new file." 1050Noninteractively, return the name of the new file."
1051 (interactive) 1051 (interactive)
1052 (let ((prompt (concat "Enter name of new Todos file " 1052 (let ((prompt (concat "Enter name of new Todo file "
1053 "(TAB or SPC to see current names): ")) 1053 "(TAB or SPC to see current names): "))
1054 file) 1054 file)
1055 (setq file (todos-read-file-name prompt)) 1055 (setq file (todo-read-file-name prompt))
1056 (with-current-buffer (get-buffer-create file) 1056 (with-current-buffer (get-buffer-create file)
1057 (erase-buffer) 1057 (erase-buffer)
1058 (write-region (point-min) (point-max) file nil 'nomessage nil t) 1058 (write-region (point-min) (point-max) file nil 'nomessage nil t)
1059 (kill-buffer file)) 1059 (kill-buffer file))
1060 (setq todos-files (funcall todos-files-function)) 1060 (setq todo-files (funcall todo-files-function))
1061 (todos-reevaluate-filelist-defcustoms) 1061 (todo-reevaluate-filelist-defcustoms)
1062 (if (called-interactively-p 'any) 1062 (if (called-interactively-p 'any)
1063 (progn 1063 (progn
1064 (set-window-buffer (selected-window) 1064 (set-window-buffer (selected-window)
1065 (set-buffer (find-file-noselect file))) 1065 (set-buffer (find-file-noselect file)))
1066 (setq todos-current-todos-file file) 1066 (setq todo-current-todo-file file)
1067 (todos-show)) 1067 (todo-show))
1068 file))) 1068 file)))
1069 1069
1070(defvar todos-edit-buffer "*Todos Edit*" 1070(defvar todo-edit-buffer "*Todo Edit*"
1071 "Name of current buffer in Todos Edit mode.") 1071 "Name of current buffer in Todo Edit mode.")
1072 1072
1073(defun todos-edit-file () 1073(defun todo-edit-file ()
1074 "Put current buffer in `todos-edit-mode'. 1074 "Put current buffer in `todo-edit-mode'.
1075This makes the entire file visible and the buffer writeable and 1075This makes the entire file visible and the buffer writeable and
1076you can use the self-insertion keys and standard Emacs editing 1076you can use the self-insertion keys and standard Emacs editing
1077commands to make changes. To return to Todos mode, type 1077commands to make changes. To return to Todo mode, type
1078\\[todos-edit-quit]. This runs a file format check, signalling 1078\\[todo-edit-quit]. This runs a file format check, signalling
1079an error if the format has become invalid. However, this check 1079an error if the format has become invalid. However, this check
1080cannot tell if the number of items changed, which could result in 1080cannot tell if the number of items changed, which could result in
1081the file containing inconsistent information. For this reason 1081the file containing inconsistent information. For this reason
1082this command should be used with caution." 1082this command should be used with caution."
1083 (interactive) 1083 (interactive)
1084 (widen) 1084 (widen)
1085 (todos-edit-mode) 1085 (todo-edit-mode)
1086 (remove-overlays) 1086 (remove-overlays)
1087 (message "%s" (substitute-command-keys 1087 (message "%s" (substitute-command-keys
1088 (concat "Type \\[todos-edit-quit] to check file format " 1088 (concat "Type \\[todo-edit-quit] to check file format "
1089 "validity and return to Todos mode.\n")))) 1089 "validity and return to Todo mode.\n"))))
1090 1090
1091(defun todos-add-category (&optional file cat) 1091(defun todo-add-category (&optional file cat)
1092 "Add a new category to a Todos file. 1092 "Add a new category to a Todo file.
1093 1093
1094Called interactively with prefix argument FILE, prompt for a file 1094Called interactively with prefix argument FILE, prompt for a file
1095and then for a new category to add to that file, otherwise prompt 1095and then for a new category to add to that file, otherwise prompt
1096just for a category to add to the current Todos file. After 1096just for a category to add to the current Todo file. After
1097adding the category, visit it in Todos mode and if option 1097adding the category, visit it in Todo mode and if option
1098`todos-add-item-if-new-category' is non-nil (the default), prompt 1098`todo-add-item-if-new-category' is non-nil (the default), prompt
1099for the first item. 1099for the first item.
1100 1100
1101Non-interactively, add category CAT to file FILE; if FILE is nil, 1101Non-interactively, add category CAT to file FILE; if FILE is nil,
1102add CAT to the current Todos file. After adding the category, 1102add CAT to the current Todo file. After adding the category,
1103return the new category number." 1103return the new category number."
1104 (interactive "P") 1104 (interactive "P")
1105 (let (catfil file0) 1105 (let (catfil file0)
@@ -1107,8 +1107,8 @@ return the new category number."
1107 ;; which means the file was just added and has no category yet. 1107 ;; which means the file was just added and has no category yet.
1108 (if (and cat (> (length cat) 0)) 1108 (if (and cat (> (length cat) 0))
1109 (setq file0 (or (and (stringp file) file) 1109 (setq file0 (or (and (stringp file) file)
1110 todos-current-todos-file)) 1110 todo-current-todo-file))
1111 (setq catfil (todos-read-category "Enter a new category name: " 1111 (setq catfil (todo-read-category "Enter a new category name: "
1112 'add (when (called-interactively-p 'any) 1112 'add (when (called-interactively-p 'any)
1113 file)) 1113 file))
1114 cat (car catfil) 1114 cat (car catfil)
@@ -1117,140 +1117,140 @@ return the new category number."
1117 file))) 1117 file)))
1118 (find-file file0) 1118 (find-file file0)
1119 (let ((counts (make-vector 4 0)) ; [todo diary done archived] 1119 (let ((counts (make-vector 4 0)) ; [todo diary done archived]
1120 (num (1+ (length todos-categories))) 1120 (num (1+ (length todo-categories)))
1121 (buffer-read-only nil)) 1121 (buffer-read-only nil))
1122 (setq todos-current-todos-file file0) 1122 (setq todo-current-todo-file file0)
1123 (setq todos-categories (append todos-categories 1123 (setq todo-categories (append todo-categories
1124 (list (cons cat counts)))) 1124 (list (cons cat counts))))
1125 (widen) 1125 (widen)
1126 (goto-char (point-max)) 1126 (goto-char (point-max))
1127 (save-excursion ; Save point for todos-category-select. 1127 (save-excursion ; Save point for todo-category-select.
1128 (insert todos-category-beg cat "\n\n" todos-category-done "\n")) 1128 (insert todo-category-beg cat "\n\n" todo-category-done "\n"))
1129 (todos-update-categories-sexp) 1129 (todo-update-categories-sexp)
1130 ;; If invoked by user, display the newly added category, if 1130 ;; If invoked by user, display the newly added category, if
1131 ;; called programmatically return the category number to the 1131 ;; called programmatically return the category number to the
1132 ;; caller. 1132 ;; caller.
1133 (if (called-interactively-p 'any) 1133 (if (called-interactively-p 'any)
1134 (progn 1134 (progn
1135 (setq todos-category-number num) 1135 (setq todo-category-number num)
1136 (todos-category-select) 1136 (todo-category-select)
1137 (when todos-add-item-if-new-category 1137 (when todo-add-item-if-new-category
1138 (todos-basic-insert-item))) 1138 (todo-basic-insert-item)))
1139 num)))) 1139 num))))
1140 1140
1141(defun todos-rename-category () 1141(defun todo-rename-category ()
1142 "Rename current Todos category. 1142 "Rename current Todo category.
1143If this file has an archive containing this category, rename the 1143If this file has an archive containing this category, rename the
1144category there as well." 1144category there as well."
1145 (interactive) 1145 (interactive)
1146 (let* ((cat (todos-current-category)) 1146 (let* ((cat (todo-current-category))
1147 (new (read-from-minibuffer 1147 (new (read-from-minibuffer
1148 (format "Rename category \"%s\" to: " cat)))) 1148 (format "Rename category \"%s\" to: " cat))))
1149 (setq new (todos-validate-name new 'category)) 1149 (setq new (todo-validate-name new 'category))
1150 (let* ((ofile todos-current-todos-file) 1150 (let* ((ofile todo-current-todo-file)
1151 (archive (concat (file-name-sans-extension ofile) ".toda")) 1151 (archive (concat (file-name-sans-extension ofile) ".toda"))
1152 (buffers (append (list ofile) 1152 (buffers (append (list ofile)
1153 (unless (zerop (todos-get-count 'archived cat)) 1153 (unless (zerop (todo-get-count 'archived cat))
1154 (list archive))))) 1154 (list archive)))))
1155 (dolist (buf buffers) 1155 (dolist (buf buffers)
1156 (with-current-buffer (find-file-noselect buf) 1156 (with-current-buffer (find-file-noselect buf)
1157 (let (buffer-read-only) 1157 (let (buffer-read-only)
1158 (setq todos-categories (todos-set-categories)) 1158 (setq todo-categories (todo-set-categories))
1159 (save-excursion 1159 (save-excursion
1160 (save-restriction 1160 (save-restriction
1161 (setcar (assoc cat todos-categories) new) 1161 (setcar (assoc cat todo-categories) new)
1162 (widen) 1162 (widen)
1163 (goto-char (point-min)) 1163 (goto-char (point-min))
1164 (todos-update-categories-sexp) 1164 (todo-update-categories-sexp)
1165 (re-search-forward (concat (regexp-quote todos-category-beg) 1165 (re-search-forward (concat (regexp-quote todo-category-beg)
1166 "\\(" (regexp-quote cat) "\\)\n") 1166 "\\(" (regexp-quote cat) "\\)\n")
1167 nil t) 1167 nil t)
1168 (replace-match new t t nil 1))))))) 1168 (replace-match new t t nil 1)))))))
1169 (force-mode-line-update)) 1169 (force-mode-line-update))
1170 (save-excursion (todos-category-select))) 1170 (save-excursion (todo-category-select)))
1171 1171
1172(defun todos-delete-category (&optional arg) 1172(defun todo-delete-category (&optional arg)
1173 "Delete current Todos category provided it is empty. 1173 "Delete current Todo category provided it is empty.
1174With ARG non-nil delete the category unconditionally, 1174With ARG non-nil delete the category unconditionally,
1175i.e. including all existing todo and done items." 1175i.e. including all existing todo and done items."
1176 (interactive "P") 1176 (interactive "P")
1177 (let* ((file todos-current-todos-file) 1177 (let* ((file todo-current-todo-file)
1178 (cat (todos-current-category)) 1178 (cat (todo-current-category))
1179 (todo (todos-get-count 'todo cat)) 1179 (todo (todo-get-count 'todo cat))
1180 (done (todos-get-count 'done cat)) 1180 (done (todo-get-count 'done cat))
1181 (archived (todos-get-count 'archived cat))) 1181 (archived (todo-get-count 'archived cat)))
1182 (if (and (not arg) 1182 (if (and (not arg)
1183 (or (> todo 0) (> done 0))) 1183 (or (> todo 0) (> done 0)))
1184 (message "%s" (substitute-command-keys 1184 (message "%s" (substitute-command-keys
1185 (concat "To delete a non-empty category, " 1185 (concat "To delete a non-empty category, "
1186 "type C-u \\[todos-delete-category]."))) 1186 "type C-u \\[todo-delete-category].")))
1187 (when (cond ((= (length todos-categories) 1) 1187 (when (cond ((= (length todo-categories) 1)
1188 (todos-y-or-n-p 1188 (todo-y-or-n-p
1189 (concat "This is the only category in this file; " 1189 (concat "This is the only category in this file; "
1190 "deleting it will also delete the file.\n" 1190 "deleting it will also delete the file.\n"
1191 "Do you want to proceed? "))) 1191 "Do you want to proceed? ")))
1192 ((> archived 0) 1192 ((> archived 0)
1193 (todos-y-or-n-p (concat "This category has archived items; " 1193 (todo-y-or-n-p (concat "This category has archived items; "
1194 "the archived category will remain\n" 1194 "the archived category will remain\n"
1195 "after deleting the todo category. " 1195 "after deleting the todo category. "
1196 "Do you still want to delete it\n" 1196 "Do you still want to delete it\n"
1197 "(see `todos-skip-archived-categories' " 1197 "(see `todo-skip-archived-categories' "
1198 "for another option)? "))) 1198 "for another option)? ")))
1199 (t 1199 (t
1200 (todos-y-or-n-p (concat "Permanently remove category \"" cat 1200 (todo-y-or-n-p (concat "Permanently remove category \"" cat
1201 "\"" (and arg " and all its entries") 1201 "\"" (and arg " and all its entries")
1202 "? ")))) 1202 "? "))))
1203 (widen) 1203 (widen)
1204 (let ((buffer-read-only) 1204 (let ((buffer-read-only)
1205 (beg (re-search-backward 1205 (beg (re-search-backward
1206 (concat "^" (regexp-quote (concat todos-category-beg cat)) 1206 (concat "^" (regexp-quote (concat todo-category-beg cat))
1207 "\n") nil t)) 1207 "\n") nil t))
1208 (end (if (re-search-forward 1208 (end (if (re-search-forward
1209 (concat "\n\\(" (regexp-quote todos-category-beg) 1209 (concat "\n\\(" (regexp-quote todo-category-beg)
1210 ".*\n\\)") nil t) 1210 ".*\n\\)") nil t)
1211 (match-beginning 1) 1211 (match-beginning 1)
1212 (point-max)))) 1212 (point-max))))
1213 (remove-overlays beg end) 1213 (remove-overlays beg end)
1214 (delete-region beg end) 1214 (delete-region beg end)
1215 (if (= (length todos-categories) 1) 1215 (if (= (length todo-categories) 1)
1216 ;; If deleted category was the only one, delete the file. 1216 ;; If deleted category was the only one, delete the file.
1217 (progn 1217 (progn
1218 (todos-reevaluate-filelist-defcustoms) 1218 (todo-reevaluate-filelist-defcustoms)
1219 ;; Skip confirming killing the archive buffer if it has been 1219 ;; Skip confirming killing the archive buffer if it has been
1220 ;; modified and not saved. 1220 ;; modified and not saved.
1221 (set-buffer-modified-p nil) 1221 (set-buffer-modified-p nil)
1222 (delete-file file) 1222 (delete-file file)
1223 (kill-buffer) 1223 (kill-buffer)
1224 (message "Deleted Todos file %s." file)) 1224 (message "Deleted Todo file %s." file))
1225 (setq todos-categories (delete (assoc cat todos-categories) 1225 (setq todo-categories (delete (assoc cat todo-categories)
1226 todos-categories)) 1226 todo-categories))
1227 (todos-update-categories-sexp) 1227 (todo-update-categories-sexp)
1228 (setq todos-category-number 1228 (setq todo-category-number
1229 (1+ (mod todos-category-number (length todos-categories)))) 1229 (1+ (mod todo-category-number (length todo-categories))))
1230 (todos-category-select) 1230 (todo-category-select)
1231 (goto-char (point-min)) 1231 (goto-char (point-min))
1232 (message "Deleted category %s." cat))))))) 1232 (message "Deleted category %s." cat)))))))
1233 1233
1234(defun todos-move-category () 1234(defun todo-move-category ()
1235 "Move current category to a different Todos file. 1235 "Move current category to a different Todo file.
1236If current category has archived items, also move those to the 1236If current category has archived items, also move those to the
1237archive of the file moved to, creating it if it does not exist." 1237archive of the file moved to, creating it if it does not exist."
1238 (interactive) 1238 (interactive)
1239 (when (or (> (length todos-categories) 1) 1239 (when (or (> (length todo-categories) 1)
1240 (todos-y-or-n-p (concat "This is the only category in this file; " 1240 (todo-y-or-n-p (concat "This is the only category in this file; "
1241 "moving it will also delete the file.\n" 1241 "moving it will also delete the file.\n"
1242 "Do you want to proceed? "))) 1242 "Do you want to proceed? ")))
1243 (let* ((ofile todos-current-todos-file) 1243 (let* ((ofile todo-current-todo-file)
1244 (cat (todos-current-category)) 1244 (cat (todo-current-category))
1245 (nfile (todos-read-file-name 1245 (nfile (todo-read-file-name
1246 "Choose a Todos file to move this category to: " nil t)) 1246 "Choose a Todo file to move this category to: " nil t))
1247 (archive (concat (file-name-sans-extension ofile) ".toda")) 1247 (archive (concat (file-name-sans-extension ofile) ".toda"))
1248 (buffers (append (list ofile) 1248 (buffers (append (list ofile)
1249 (unless (zerop (todos-get-count 'archived cat)) 1249 (unless (zerop (todo-get-count 'archived cat))
1250 (list archive)))) 1250 (list archive))))
1251 new) 1251 new)
1252 (while (equal (file-truename nfile) (file-truename ofile)) 1252 (while (equal (file-truename nfile) (file-truename ofile))
1253 (setq nfile (todos-read-file-name 1253 (setq nfile (todo-read-file-name
1254 "Choose a file distinct from this file: " nil t))) 1254 "Choose a file distinct from this file: " nil t)))
1255 (dolist (buf buffers) 1255 (dolist (buf buffers)
1256 (with-current-buffer (find-file-noselect buf) 1256 (with-current-buffer (find-file-noselect buf)
@@ -1258,29 +1258,29 @@ archive of the file moved to, creating it if it does not exist."
1258 (goto-char (point-max)) 1258 (goto-char (point-max))
1259 (let* ((beg (re-search-backward 1259 (let* ((beg (re-search-backward
1260 (concat "^" 1260 (concat "^"
1261 (regexp-quote (concat todos-category-beg cat)) 1261 (regexp-quote (concat todo-category-beg cat))
1262 "$") 1262 "$")
1263 nil t)) 1263 nil t))
1264 (end (if (re-search-forward 1264 (end (if (re-search-forward
1265 (concat "^" (regexp-quote todos-category-beg)) 1265 (concat "^" (regexp-quote todo-category-beg))
1266 nil t 2) 1266 nil t 2)
1267 (match-beginning 0) 1267 (match-beginning 0)
1268 (point-max))) 1268 (point-max)))
1269 (content (buffer-substring-no-properties beg end)) 1269 (content (buffer-substring-no-properties beg end))
1270 (counts (cdr (assoc cat todos-categories))) 1270 (counts (cdr (assoc cat todo-categories)))
1271 buffer-read-only) 1271 buffer-read-only)
1272 ;; Move the category to the new file. Also update or create 1272 ;; Move the category to the new file. Also update or create
1273 ;; archive file if necessary. 1273 ;; archive file if necessary.
1274 (with-current-buffer 1274 (with-current-buffer
1275 (find-file-noselect 1275 (find-file-noselect
1276 ;; Regenerate todos-archives in case there 1276 ;; Regenerate todo-archives in case there
1277 ;; is a newly created archive. 1277 ;; is a newly created archive.
1278 (if (member buf (funcall todos-files-function t)) 1278 (if (member buf (funcall todo-files-function t))
1279 (concat (file-name-sans-extension nfile) ".toda") 1279 (concat (file-name-sans-extension nfile) ".toda")
1280 nfile)) 1280 nfile))
1281 (let* ((nfile-short (todos-short-file-name nfile)) 1281 (let* ((nfile-short (todo-short-file-name nfile))
1282 (prompt (concat 1282 (prompt (concat
1283 (format "Todos file \"%s\" already has " 1283 (format "Todo file \"%s\" already has "
1284 nfile-short) 1284 nfile-short)
1285 (format "the category \"%s\";\n" cat) 1285 (format "the category \"%s\";\n" cat)
1286 "enter a new category name: ")) 1286 "enter a new category name: "))
@@ -1290,60 +1290,60 @@ archive of the file moved to, creating it if it does not exist."
1290 (insert content) 1290 (insert content)
1291 ;; If the file moved to has a category with the same 1291 ;; If the file moved to has a category with the same
1292 ;; name, rename the moved category. 1292 ;; name, rename the moved category.
1293 (when (assoc cat todos-categories) 1293 (when (assoc cat todo-categories)
1294 (unless (member (file-truename (buffer-file-name)) 1294 (unless (member (file-truename (buffer-file-name))
1295 (funcall todos-files-function t)) 1295 (funcall todo-files-function t))
1296 (setq new (read-from-minibuffer prompt)) 1296 (setq new (read-from-minibuffer prompt))
1297 (setq new (todos-validate-name new 'category)))) 1297 (setq new (todo-validate-name new 'category))))
1298 ;; Replace old with new name in Todos and archive files. 1298 ;; Replace old with new name in Todo and archive files.
1299 (when new 1299 (when new
1300 (goto-char (point-max)) 1300 (goto-char (point-max))
1301 (re-search-backward 1301 (re-search-backward
1302 (concat "^" (regexp-quote todos-category-beg) 1302 (concat "^" (regexp-quote todo-category-beg)
1303 "\\(" (regexp-quote cat) "\\)$") nil t) 1303 "\\(" (regexp-quote cat) "\\)$") nil t)
1304 (replace-match new nil nil nil 1))) 1304 (replace-match new nil nil nil 1)))
1305 (setq todos-categories 1305 (setq todo-categories
1306 (append todos-categories (list (cons new counts)))) 1306 (append todo-categories (list (cons new counts))))
1307 (todos-update-categories-sexp) 1307 (todo-update-categories-sexp)
1308 ;; If archive was just created, save it to avoid "File 1308 ;; If archive was just created, save it to avoid "File
1309 ;; <xyz> no longer exists!" message on invoking 1309 ;; <xyz> no longer exists!" message on invoking
1310 ;; `todos-view-archived-items'. 1310 ;; `todo-view-archived-items'.
1311 (unless (file-exists-p (buffer-file-name)) 1311 (unless (file-exists-p (buffer-file-name))
1312 (save-buffer)) 1312 (save-buffer))
1313 (todos-category-number (or new cat)) 1313 (todo-category-number (or new cat))
1314 (todos-category-select)) 1314 (todo-category-select))
1315 ;; Delete the category from the old file, and if that was the 1315 ;; Delete the category from the old file, and if that was the
1316 ;; last category, delete the file. Also handle archive file 1316 ;; last category, delete the file. Also handle archive file
1317 ;; if necessary. 1317 ;; if necessary.
1318 (remove-overlays beg end) 1318 (remove-overlays beg end)
1319 (delete-region beg end) 1319 (delete-region beg end)
1320 (goto-char (point-min)) 1320 (goto-char (point-min))
1321 ;; Put point after todos-categories sexp. 1321 ;; Put point after todo-categories sexp.
1322 (forward-line) 1322 (forward-line)
1323 (if (eobp) ; Aside from sexp, file is empty. 1323 (if (eobp) ; Aside from sexp, file is empty.
1324 (progn 1324 (progn
1325 ;; Skip confirming killing the archive buffer. 1325 ;; Skip confirming killing the archive buffer.
1326 (set-buffer-modified-p nil) 1326 (set-buffer-modified-p nil)
1327 (delete-file todos-current-todos-file) 1327 (delete-file todo-current-todo-file)
1328 (kill-buffer) 1328 (kill-buffer)
1329 (when (member todos-current-todos-file todos-files) 1329 (when (member todo-current-todo-file todo-files)
1330 (todos-reevaluate-filelist-defcustoms))) 1330 (todo-reevaluate-filelist-defcustoms)))
1331 (setq todos-categories (delete (assoc cat todos-categories) 1331 (setq todo-categories (delete (assoc cat todo-categories)
1332 todos-categories)) 1332 todo-categories))
1333 (todos-update-categories-sexp) 1333 (todo-update-categories-sexp)
1334 (todos-category-select))))) 1334 (todo-category-select)))))
1335 (set-window-buffer (selected-window) 1335 (set-window-buffer (selected-window)
1336 (set-buffer (find-file-noselect nfile))) 1336 (set-buffer (find-file-noselect nfile)))
1337 (todos-category-number (or new cat)) 1337 (todo-category-number (or new cat))
1338 (todos-category-select)))) 1338 (todo-category-select))))
1339 1339
1340(defun todos-merge-category (&optional file) 1340(defun todo-merge-category (&optional file)
1341 "Merge current category into another existing category. 1341 "Merge current category into another existing category.
1342 1342
1343With prefix argument FILE, prompt for a specific Todos file and 1343With prefix argument FILE, prompt for a specific Todo file and
1344choose (with TAB completion) a category in it to merge into; 1344choose (with TAB completion) a category in it to merge into;
1345otherwise, choose and merge into a category in either the 1345otherwise, choose and merge into a category in either the
1346current Todos file or a file in `todos-category-completions-files'. 1346current Todo file or a file in `todo-category-completions-files'.
1347 1347
1348After merging, the current category's todo and done items are 1348After merging, the current category's todo and done items are
1349appended to the chosen goal category's todo and done items, 1349appended to the chosen goal category's todo and done items,
@@ -1355,9 +1355,9 @@ the former are merged to the latter. If only the first category
1355has archived items, the archived category is renamed to the goal 1355has archived items, the archived category is renamed to the goal
1356category." 1356category."
1357 (interactive "P") 1357 (interactive "P")
1358 (let* ((tfile todos-current-todos-file) 1358 (let* ((tfile todo-current-todo-file)
1359 (cat (todos-current-category)) 1359 (cat (todo-current-category))
1360 (cat+file (todos-read-category "Merge into category: " 'todo file)) 1360 (cat+file (todo-read-category "Merge into category: " 'todo file))
1361 (goal (car cat+file)) 1361 (goal (car cat+file))
1362 (gfile (cdr cat+file)) 1362 (gfile (cdr cat+file))
1363 (archive (concat (file-name-sans-extension (if file gfile tfile)) 1363 (archive (concat (file-name-sans-extension (if file gfile tfile))
@@ -1369,18 +1369,18 @@ category."
1369 (let* ((buffer-read-only nil) 1369 (let* ((buffer-read-only nil)
1370 (cbeg (progn 1370 (cbeg (progn
1371 (re-search-backward 1371 (re-search-backward
1372 (concat "^" (regexp-quote todos-category-beg)) nil t) 1372 (concat "^" (regexp-quote todo-category-beg)) nil t)
1373 (point-marker))) 1373 (point-marker)))
1374 (tbeg (progn (forward-line) (point-marker))) 1374 (tbeg (progn (forward-line) (point-marker)))
1375 (dbeg (progn 1375 (dbeg (progn
1376 (re-search-forward 1376 (re-search-forward
1377 (concat "^" (regexp-quote todos-category-done)) nil t) 1377 (concat "^" (regexp-quote todo-category-done)) nil t)
1378 (forward-line) (point-marker))) 1378 (forward-line) (point-marker)))
1379 ;; Omit empty line between todo and done items. 1379 ;; Omit empty line between todo and done items.
1380 (tend (progn (forward-line -2) (point-marker))) 1380 (tend (progn (forward-line -2) (point-marker)))
1381 (cend (progn 1381 (cend (progn
1382 (if (re-search-forward 1382 (if (re-search-forward
1383 (concat "^" (regexp-quote todos-category-beg)) nil t) 1383 (concat "^" (regexp-quote todo-category-beg)) nil t)
1384 (progn 1384 (progn
1385 (goto-char (match-beginning 0)) 1385 (goto-char (match-beginning 0))
1386 (point-marker)) 1386 (point-marker))
@@ -1391,28 +1391,28 @@ category."
1391 ;; Merge any todo items. 1391 ;; Merge any todo items.
1392 (unless (zerop (length todo)) 1392 (unless (zerop (length todo))
1393 (re-search-forward 1393 (re-search-forward
1394 (concat "^" (regexp-quote (concat todos-category-beg goal)) "$") 1394 (concat "^" (regexp-quote (concat todo-category-beg goal)) "$")
1395 nil t) 1395 nil t)
1396 (re-search-forward 1396 (re-search-forward
1397 (concat "^" (regexp-quote todos-category-done)) nil t) 1397 (concat "^" (regexp-quote todo-category-done)) nil t)
1398 (forward-line -1) 1398 (forward-line -1)
1399 (setq here (point-marker)) 1399 (setq here (point-marker))
1400 (insert todo) 1400 (insert todo)
1401 (todos-update-count 'todo (todos-get-count 'todo cat) goal)) 1401 (todo-update-count 'todo (todo-get-count 'todo cat) goal))
1402 ;; Merge any done items. 1402 ;; Merge any done items.
1403 (unless (zerop (length done)) 1403 (unless (zerop (length done))
1404 (goto-char (if (re-search-forward 1404 (goto-char (if (re-search-forward
1405 (concat "^" (regexp-quote todos-category-beg)) nil t) 1405 (concat "^" (regexp-quote todo-category-beg)) nil t)
1406 (match-beginning 0) 1406 (match-beginning 0)
1407 (point-max))) 1407 (point-max)))
1408 (when (zerop (length todo)) (setq here (point-marker))) 1408 (when (zerop (length todo)) (setq here (point-marker)))
1409 (insert done) 1409 (insert done)
1410 (todos-update-count 'done (todos-get-count 'done cat) goal)) 1410 (todo-update-count 'done (todo-get-count 'done cat) goal))
1411 (remove-overlays cbeg cend) 1411 (remove-overlays cbeg cend)
1412 (delete-region cbeg cend) 1412 (delete-region cbeg cend)
1413 (setq todos-categories (delete (assoc cat todos-categories) 1413 (setq todo-categories (delete (assoc cat todo-categories)
1414 todos-categories)) 1414 todo-categories))
1415 (todos-update-categories-sexp) 1415 (todo-update-categories-sexp)
1416 (mapc (lambda (m) (set-marker m nil)) (list cbeg tbeg dbeg tend cend)))) 1416 (mapc (lambda (m) (set-marker m nil)) (list cbeg tbeg dbeg tend cend))))
1417 (when (file-exists-p archive) 1417 (when (file-exists-p archive)
1418 ;; Merge in archive file. 1418 ;; Merge in archive file.
@@ -1423,23 +1423,23 @@ category."
1423 (cbeg (save-excursion 1423 (cbeg (save-excursion
1424 (when (re-search-forward 1424 (when (re-search-forward
1425 (concat "^" (regexp-quote 1425 (concat "^" (regexp-quote
1426 (concat todos-category-beg cat)) "$") 1426 (concat todo-category-beg cat)) "$")
1427 nil t) 1427 nil t)
1428 (goto-char (match-beginning 0)) 1428 (goto-char (match-beginning 0))
1429 (point-marker)))) 1429 (point-marker))))
1430 (gbeg (save-excursion 1430 (gbeg (save-excursion
1431 (when (re-search-forward 1431 (when (re-search-forward
1432 (concat "^" (regexp-quote 1432 (concat "^" (regexp-quote
1433 (concat todos-category-beg goal)) "$") 1433 (concat todo-category-beg goal)) "$")
1434 nil t) 1434 nil t)
1435 (goto-char (match-beginning 0)) 1435 (goto-char (match-beginning 0))
1436 (point-marker)))) 1436 (point-marker))))
1437 cend carch) 1437 cend carch)
1438 (when cbeg 1438 (when cbeg
1439 (setq archived-count (todos-get-count 'done cat)) 1439 (setq archived-count (todo-get-count 'done cat))
1440 (setq cend (save-excursion 1440 (setq cend (save-excursion
1441 (if (re-search-forward 1441 (if (re-search-forward
1442 (concat "^" (regexp-quote todos-category-beg)) 1442 (concat "^" (regexp-quote todo-category-beg))
1443 nil t) 1443 nil t)
1444 (match-beginning 0) 1444 (match-beginning 0)
1445 (point-max)))) 1445 (point-max))))
@@ -1451,7 +1451,7 @@ category."
1451 (if gbeg 1451 (if gbeg
1452 (progn 1452 (progn
1453 (goto-char (if (re-search-forward 1453 (goto-char (if (re-search-forward
1454 (concat "^" (regexp-quote todos-category-beg)) 1454 (concat "^" (regexp-quote todo-category-beg))
1455 nil t) 1455 nil t)
1456 (match-beginning 0) 1456 (match-beginning 0)
1457 (point-max))) 1457 (point-max)))
@@ -1461,17 +1461,17 @@ category."
1461 (goto-char cbeg) 1461 (goto-char cbeg)
1462 (search-forward cat) 1462 (search-forward cat)
1463 (replace-match goal)) 1463 (replace-match goal))
1464 (setq todos-categories (todos-make-categories-list t)) 1464 (setq todo-categories (todo-make-categories-list t))
1465 (todos-update-categories-sexp))))) 1465 (todo-update-categories-sexp)))))
1466 (with-current-buffer (get-file-buffer tfile) 1466 (with-current-buffer (get-file-buffer tfile)
1467 (when archived-count 1467 (when archived-count
1468 (unless (zerop archived-count) 1468 (unless (zerop archived-count)
1469 (todos-update-count 'archived archived-count goal) 1469 (todo-update-count 'archived archived-count goal)
1470 (todos-update-categories-sexp))) 1470 (todo-update-categories-sexp)))
1471 (todos-category-number goal) 1471 (todo-category-number goal)
1472 ;; If there are only merged done items, show them. 1472 ;; If there are only merged done items, show them.
1473 (let ((todos-show-with-done (zerop (todos-get-count 'todo goal)))) 1473 (let ((todo-show-with-done (zerop (todo-get-count 'todo goal))))
1474 (todos-category-select) 1474 (todo-category-select)
1475 ;; Put point on the first merged item. 1475 ;; Put point on the first merged item.
1476 (goto-char here))) 1476 (goto-char here)))
1477 (set-marker here nil))) 1477 (set-marker here nil)))
@@ -1480,132 +1480,132 @@ category."
1480;;; Item editing 1480;;; Item editing
1481;; ----------------------------------------------------------------------------- 1481;; -----------------------------------------------------------------------------
1482 1482
1483(defcustom todos-include-in-diary nil 1483(defcustom todo-include-in-diary nil
1484 "Non-nil to allow new Todo items to be included in the diary." 1484 "Non-nil to allow new Todo items to be included in the diary."
1485 :type 'boolean 1485 :type 'boolean
1486 :group 'todos-edit) 1486 :group 'todo-edit)
1487 1487
1488(defcustom todos-diary-nonmarking nil 1488(defcustom todo-diary-nonmarking nil
1489 "Non-nil to insert new Todo diary items as nonmarking by default. 1489 "Non-nil to insert new Todo diary items as nonmarking by default.
1490This appends `diary-nonmarking-symbol' to the front of an item on 1490This appends `diary-nonmarking-symbol' to the front of an item on
1491insertion provided it doesn't begin with `todos-nondiary-marker'." 1491insertion provided it doesn't begin with `todo-nondiary-marker'."
1492 :type 'boolean 1492 :type 'boolean
1493 :group 'todos-edit) 1493 :group 'todo-edit)
1494 1494
1495(defcustom todos-always-add-time-string nil 1495(defcustom todo-always-add-time-string nil
1496 "Non-nil adds current time to a new item's date header by default. 1496 "Non-nil adds current time to a new item's date header by default.
1497When the Todos insertion commands have a non-nil \"maybe-notime\" 1497When the Todo insertion commands have a non-nil \"maybe-notime\"
1498argument, this reverses the effect of 1498argument, this reverses the effect of
1499`todos-always-add-time-string': if t, these commands omit the 1499`todo-always-add-time-string': if t, these commands omit the
1500current time, if nil, they include it." 1500current time, if nil, they include it."
1501 :type 'boolean 1501 :type 'boolean
1502 :group 'todos-edit) 1502 :group 'todo-edit)
1503 1503
1504(defcustom todos-use-only-highlighted-region t 1504(defcustom todo-use-only-highlighted-region t
1505 "Non-nil to enable inserting only highlighted region as new item." 1505 "Non-nil to enable inserting only highlighted region as new item."
1506 :type 'boolean 1506 :type 'boolean
1507 :group 'todos-edit) 1507 :group 'todo-edit)
1508 1508
1509(defcustom todos-item-mark "*" 1509(defcustom todo-item-mark "*"
1510 "String used to mark items. 1510 "String used to mark items.
1511To ensure item marking works, change the value of this option 1511To ensure item marking works, change the value of this option
1512only when no items are marked." 1512only when no items are marked."
1513 :type '(string :validate 1513 :type '(string :validate
1514 (lambda (widget) 1514 (lambda (widget)
1515 (when (string= (widget-value widget) todos-prefix) 1515 (when (string= (widget-value widget) todo-prefix)
1516 (widget-put 1516 (widget-put
1517 widget :error 1517 widget :error
1518 "Invalid value: must be distinct from `todos-prefix'") 1518 "Invalid value: must be distinct from `todo-prefix'")
1519 widget))) 1519 widget)))
1520 :set (lambda (symbol value) 1520 :set (lambda (symbol value)
1521 (custom-set-default symbol (propertize value 'face 'todos-mark))) 1521 (custom-set-default symbol (propertize value 'face 'todo-mark)))
1522 :group 'todos-edit) 1522 :group 'todo-edit)
1523 1523
1524(defcustom todos-comment-string "COMMENT" 1524(defcustom todo-comment-string "COMMENT"
1525 "String inserted before optional comment appended to done item." 1525 "String inserted before optional comment appended to done item."
1526 :type 'string 1526 :type 'string
1527 :initialize 'custom-initialize-default 1527 :initialize 'custom-initialize-default
1528 :set 'todos-reset-comment-string 1528 :set 'todo-reset-comment-string
1529 :group 'todos-edit) 1529 :group 'todo-edit)
1530 1530
1531(defcustom todos-undo-item-omit-comment 'ask 1531(defcustom todo-undo-item-omit-comment 'ask
1532 "Whether to omit done item comment on undoing the item. 1532 "Whether to omit done item comment on undoing the item.
1533Nil means never omit the comment, t means always omit it, `ask' 1533Nil means never omit the comment, t means always omit it, `ask'
1534means prompt user and omit comment only on confirmation." 1534means prompt user and omit comment only on confirmation."
1535 :type '(choice (const :tag "Never" nil) 1535 :type '(choice (const :tag "Never" nil)
1536 (const :tag "Always" t) 1536 (const :tag "Always" t)
1537 (const :tag "Ask" ask)) 1537 (const :tag "Ask" ask))
1538 :group 'todos-edit) 1538 :group 'todo-edit)
1539 1539
1540(defun todos-toggle-mark-item (&optional n) 1540(defun todo-toggle-mark-item (&optional n)
1541 "Mark item with `todos-item-mark' if unmarked, otherwise unmark it. 1541 "Mark item with `todo-item-mark' if unmarked, otherwise unmark it.
1542With a positive numerical prefix argument N, change the 1542With a positive numerical prefix argument N, change the
1543marking of the next N items." 1543marking of the next N items."
1544 (interactive "p") 1544 (interactive "p")
1545 (when (todos-item-string) 1545 (when (todo-item-string)
1546 (unless (> n 1) (setq n 1)) 1546 (unless (> n 1) (setq n 1))
1547 (dotimes (i n) 1547 (dotimes (i n)
1548 (let* ((cat (todos-current-category)) 1548 (let* ((cat (todo-current-category))
1549 (marks (assoc cat todos-categories-with-marks)) 1549 (marks (assoc cat todo-categories-with-marks))
1550 (ov (progn 1550 (ov (progn
1551 (unless (looking-at todos-item-start) 1551 (unless (looking-at todo-item-start)
1552 (todos-item-start)) 1552 (todo-item-start))
1553 (todos-get-overlay 'prefix))) 1553 (todo-get-overlay 'prefix)))
1554 (pref (overlay-get ov 'before-string))) 1554 (pref (overlay-get ov 'before-string)))
1555 (if (todos-marked-item-p) 1555 (if (todo-marked-item-p)
1556 (progn 1556 (progn
1557 (overlay-put ov 'before-string (substring pref 1)) 1557 (overlay-put ov 'before-string (substring pref 1))
1558 (if (= (cdr marks) 1) ; Deleted last mark in this category. 1558 (if (= (cdr marks) 1) ; Deleted last mark in this category.
1559 (setq todos-categories-with-marks 1559 (setq todo-categories-with-marks
1560 (assq-delete-all cat todos-categories-with-marks)) 1560 (assq-delete-all cat todo-categories-with-marks))
1561 (setcdr marks (1- (cdr marks))))) 1561 (setcdr marks (1- (cdr marks)))))
1562 (overlay-put ov 'before-string (concat todos-item-mark pref)) 1562 (overlay-put ov 'before-string (concat todo-item-mark pref))
1563 (if marks 1563 (if marks
1564 (setcdr marks (1+ (cdr marks))) 1564 (setcdr marks (1+ (cdr marks)))
1565 (push (cons cat 1) todos-categories-with-marks)))) 1565 (push (cons cat 1) todo-categories-with-marks))))
1566 (todos-forward-item)))) 1566 (todo-forward-item))))
1567 1567
1568(defun todos-mark-category () 1568(defun todo-mark-category ()
1569 "Mark all visiblw items in this category with `todos-item-mark'." 1569 "Mark all visiblw items in this category with `todo-item-mark'."
1570 (interactive) 1570 (interactive)
1571 (let* ((cat (todos-current-category)) 1571 (let* ((cat (todo-current-category))
1572 (marks (assoc cat todos-categories-with-marks))) 1572 (marks (assoc cat todo-categories-with-marks)))
1573 (save-excursion 1573 (save-excursion
1574 (goto-char (point-min)) 1574 (goto-char (point-min))
1575 (while (not (eobp)) 1575 (while (not (eobp))
1576 (let* ((ov (todos-get-overlay 'prefix)) 1576 (let* ((ov (todo-get-overlay 'prefix))
1577 (pref (overlay-get ov 'before-string))) 1577 (pref (overlay-get ov 'before-string)))
1578 (unless (todos-marked-item-p) 1578 (unless (todo-marked-item-p)
1579 (overlay-put ov 'before-string (concat todos-item-mark pref)) 1579 (overlay-put ov 'before-string (concat todo-item-mark pref))
1580 (if marks 1580 (if marks
1581 (setcdr marks (1+ (cdr marks))) 1581 (setcdr marks (1+ (cdr marks)))
1582 (push (cons cat 1) todos-categories-with-marks)))) 1582 (push (cons cat 1) todo-categories-with-marks))))
1583 (todos-forward-item))))) 1583 (todo-forward-item)))))
1584 1584
1585(defun todos-unmark-category () 1585(defun todo-unmark-category ()
1586 "Remove `todos-item-mark' from all visible items in this category." 1586 "Remove `todo-item-mark' from all visible items in this category."
1587 (interactive) 1587 (interactive)
1588 (let* ((cat (todos-current-category)) 1588 (let* ((cat (todo-current-category))
1589 (marks (assoc cat todos-categories-with-marks))) 1589 (marks (assoc cat todo-categories-with-marks)))
1590 (save-excursion 1590 (save-excursion
1591 (goto-char (point-min)) 1591 (goto-char (point-min))
1592 (while (not (eobp)) 1592 (while (not (eobp))
1593 (let* ((ov (todos-get-overlay 'prefix)) 1593 (let* ((ov (todo-get-overlay 'prefix))
1594 ;; No overlay on empty line between todo and done items. 1594 ;; No overlay on empty line between todo and done items.
1595 (pref (when ov (overlay-get ov 'before-string)))) 1595 (pref (when ov (overlay-get ov 'before-string))))
1596 (when (todos-marked-item-p) 1596 (when (todo-marked-item-p)
1597 (overlay-put ov 'before-string (substring pref 1))) 1597 (overlay-put ov 'before-string (substring pref 1)))
1598 (todos-forward-item)))) 1598 (todo-forward-item))))
1599 (setq todos-categories-with-marks 1599 (setq todo-categories-with-marks
1600 (delq marks todos-categories-with-marks)))) 1600 (delq marks todo-categories-with-marks))))
1601 1601
1602(defvar todos-date-from-calendar nil 1602(defvar todo-date-from-calendar nil
1603 "Helper variable for setting item date from the Emacs Calendar.") 1603 "Helper variable for setting item date from the Emacs Calendar.")
1604 1604
1605(defun todos-basic-insert-item (&optional arg diary nonmarking date-type time 1605(defun todo-basic-insert-item (&optional arg diary nonmarking date-type time
1606 region-or-here) 1606 region-or-here)
1607 "Insert a new Todo item into a category. 1607 "Insert a new Todo item into a category.
1608This is the function from which the generated Todos item 1608This is the function from which the generated Todo item
1609insertion commands derive. 1609insertion commands derive.
1610 1610
1611The generated commands have mnenomic key bindings based on the 1611The generated commands have mnenomic key bindings based on the
@@ -1625,10 +1625,10 @@ is not given by HERE but by prompting.
1625In command invocations, ARG is passed as a prefix argument as 1625In command invocations, ARG is passed as a prefix argument as
1626follows. With no prefix argument, add the item to the current 1626follows. With no prefix argument, add the item to the current
1627category; with one prefix argument (`C-u'), prompt for a category 1627category; with one prefix argument (`C-u'), prompt for a category
1628from the current Todos file; with two prefix arguments (`C-u C-u'), 1628from the current Todo file; with two prefix arguments (`C-u C-u'),
1629first prompt for a Todos file, then a category in that file. If 1629first prompt for a Todo file, then a category in that file. If
1630a non-existing category is entered, ask whether to add it to the 1630a non-existing category is entered, ask whether to add it to the
1631Todos file; if answered affirmatively, add the category and 1631Todo file; if answered affirmatively, add the category and
1632insert the item there. 1632insert the item there.
1633 1633
1634The remaining arguments are set or left nil by the generated item 1634The remaining arguments are set or left nil by the generated item
@@ -1636,16 +1636,16 @@ insertion commands; their meanings are described in the follows
1636paragraphs. 1636paragraphs.
1637 1637
1638When argument DIARY is non-nil, this overrides the intent of the 1638When argument DIARY is non-nil, this overrides the intent of the
1639user option `todos-include-in-diary' for this item: if 1639user option `todo-include-in-diary' for this item: if
1640`todos-include-in-diary' is nil, include the item in the Fancy 1640`todo-include-in-diary' is nil, include the item in the Fancy
1641Diary display, and if it is non-nil, exclude the item from the 1641Diary display, and if it is non-nil, exclude the item from the
1642Fancy Diary display. When DIARY is nil, `todos-include-in-diary' 1642Fancy Diary display. When DIARY is nil, `todo-include-in-diary'
1643has its intended effect. 1643has its intended effect.
1644 1644
1645When the item is included in the Fancy Diary display and the 1645When the item is included in the Fancy Diary display and the
1646argument NONMARKING is non-nil, this overrides the intent of the 1646argument NONMARKING is non-nil, this overrides the intent of the
1647user option `todos-diary-nonmarking' for this item: if 1647user option `todo-diary-nonmarking' for this item: if
1648`todos-diary-nonmarking' is nil, append `diary-nonmarking-symbol' 1648`todo-diary-nonmarking' is nil, append `diary-nonmarking-symbol'
1649to the item, and if it is non-nil, omit `diary-nonmarking-symbol'. 1649to the item, and if it is non-nil, omit `diary-nonmarking-symbol'.
1650 1650
1651The argument DATE-TYPE determines the content of the item's 1651The argument DATE-TYPE determines the content of the item's
@@ -1655,9 +1655,9 @@ mandatory date header string and how it is added:
1655 date, in the format set by `calendar-date-display-form', 1655 date, in the format set by `calendar-date-display-form',
1656 becomes the date in the header. 1656 becomes the date in the header.
1657- If DATE-TYPE is a string matching the regexp 1657- If DATE-TYPE is a string matching the regexp
1658 `todos-date-pattern', that string becomes the date in the 1658 `todo-date-pattern', that string becomes the date in the
1659 header. This case is for the command 1659 header. This case is for the command
1660 `todos-insert-item-from-calendar' which is called from the 1660 `todo-insert-item-from-calendar' which is called from the
1661 Calendar. 1661 Calendar.
1662- If DATE-TYPE is the symbol `date', the header contains the date 1662- If DATE-TYPE is the symbol `date', the header contains the date
1663 in the format set by `calendar-date-display-form', with year, 1663 in the format set by `calendar-date-display-form', with year,
@@ -1673,10 +1673,10 @@ mandatory date header string and how it is added:
1673With non-nil argument TIME prompt for a time string, which must 1673With non-nil argument TIME prompt for a time string, which must
1674match `diary-time-regexp'. Typing `<return>' at the prompt 1674match `diary-time-regexp'. Typing `<return>' at the prompt
1675returns the current time, if the user option 1675returns the current time, if the user option
1676`todos-always-add-time-string' is non-nil, otherwise the empty 1676`todo-always-add-time-string' is non-nil, otherwise the empty
1677string (i.e., no time string). If TIME is absent or nil, add or 1677string (i.e., no time string). If TIME is absent or nil, add or
1678omit the current time string according as 1678omit the current time string according as
1679`todos-always-add-time-string' is non-nil or nil, respectively. 1679`todo-always-add-time-string' is non-nil or nil, respectively.
1680 1680
1681The argument REGION-OR-HERE determines the source and location of 1681The argument REGION-OR-HERE determines the source and location of
1682the new item: 1682the new item:
@@ -1691,7 +1691,7 @@ the new item:
1691 insert the new item as the first item in the category. 1691 insert the new item as the first item in the category.
1692- If REGION-OR-HERE is the symbol `region', use the region of the 1692- If REGION-OR-HERE is the symbol `region', use the region of the
1693 current buffer as the text of the new item, depending on the 1693 current buffer as the text of the new item, depending on the
1694 value of user option `todos-use-only-highlighted-region': if 1694 value of user option `todo-use-only-highlighted-region': if
1695 this is non-nil, then use the region only when it is 1695 this is non-nil, then use the region only when it is
1696 highlighted; otherwise, use the region regardless of 1696 highlighted; otherwise, use the region regardless of
1697 highlighting. An error is signalled if there is no region in 1697 highlighting. An error is signalled if there is no region in
@@ -1701,32 +1701,32 @@ the new item:
1701- If REGION-OR-HERE has any other value (in particular, nil or 1701- If REGION-OR-HERE has any other value (in particular, nil or
1702 none), prompt for the text and the item's priority, and insert 1702 none), prompt for the text and the item's priority, and insert
1703 the item accordingly." 1703 the item accordingly."
1704 ;; If invoked outside of Todos mode and there is not yet any Todos 1704 ;; If invoked outside of Todo mode and there is not yet any Todo
1705 ;; file, initialize one. 1705 ;; file, initialize one.
1706 (if (null todos-files) 1706 (if (null todo-files)
1707 (todos-show) 1707 (todo-show)
1708 (let ((region (eq region-or-here 'region)) 1708 (let ((region (eq region-or-here 'region))
1709 (here (eq region-or-here 'here))) 1709 (here (eq region-or-here 'here)))
1710 (when region 1710 (when region
1711 (let (use-empty-active-region) 1711 (let (use-empty-active-region)
1712 (unless (and todos-use-only-highlighted-region (use-region-p)) 1712 (unless (and todo-use-only-highlighted-region (use-region-p))
1713 (user-error "There is no active region")))) 1713 (user-error "There is no active region"))))
1714 (let* ((obuf (current-buffer)) 1714 (let* ((obuf (current-buffer))
1715 (ocat (todos-current-category)) 1715 (ocat (todo-current-category))
1716 (opoint (point)) 1716 (opoint (point))
1717 (todos-mm (eq major-mode 'todos-mode)) 1717 (todo-mm (eq major-mode 'todo-mode))
1718 (cat+file (cond ((equal arg '(4)) 1718 (cat+file (cond ((equal arg '(4))
1719 (todos-read-category "Insert in category: ")) 1719 (todo-read-category "Insert in category: "))
1720 ((equal arg '(16)) 1720 ((equal arg '(16))
1721 (todos-read-category "Insert in category: " 1721 (todo-read-category "Insert in category: "
1722 nil 'file)) 1722 nil 'file))
1723 (t 1723 (t
1724 (cons (todos-current-category) 1724 (cons (todo-current-category)
1725 (or todos-current-todos-file 1725 (or todo-current-todo-file
1726 (and todos-show-current-file 1726 (and todo-show-current-file
1727 todos-global-current-todos-file) 1727 todo-global-current-todo-file)
1728 (todos-absolute-file-name 1728 (todo-absolute-file-name
1729 todos-default-todos-file)))))) 1729 todo-default-todo-file))))))
1730 (cat (car cat+file)) 1730 (cat (car cat+file))
1731 (file (cdr cat+file)) 1731 (file (cdr cat+file))
1732 (new-item (if region 1732 (new-item (if region
@@ -1735,54 +1735,54 @@ the new item:
1735 (read-from-minibuffer "Todo item: "))) 1735 (read-from-minibuffer "Todo item: ")))
1736 (date-string (cond 1736 (date-string (cond
1737 ((eq date-type 'date) 1737 ((eq date-type 'date)
1738 (todos-read-date)) 1738 (todo-read-date))
1739 ((eq date-type 'dayname) 1739 ((eq date-type 'dayname)
1740 (todos-read-dayname)) 1740 (todo-read-dayname))
1741 ((eq date-type 'calendar) 1741 ((eq date-type 'calendar)
1742 (setq todos-date-from-calendar t) 1742 (setq todo-date-from-calendar t)
1743 (or (todos-set-date-from-calendar) 1743 (or (todo-set-date-from-calendar)
1744 ;; If user exits Calendar before choosing 1744 ;; If user exits Calendar before choosing
1745 ;; a date, cancel item insertion. 1745 ;; a date, cancel item insertion.
1746 (keyboard-quit))) 1746 (keyboard-quit)))
1747 ((and (stringp date-type) 1747 ((and (stringp date-type)
1748 (string-match todos-date-pattern date-type)) 1748 (string-match todo-date-pattern date-type))
1749 (setq todos-date-from-calendar date-type) 1749 (setq todo-date-from-calendar date-type)
1750 (todos-set-date-from-calendar)) 1750 (todo-set-date-from-calendar))
1751 (t 1751 (t
1752 (calendar-date-string 1752 (calendar-date-string
1753 (calendar-current-date) t t)))) 1753 (calendar-current-date) t t))))
1754 (time-string (or (and time (todos-read-time)) 1754 (time-string (or (and time (todo-read-time))
1755 (and todos-always-add-time-string 1755 (and todo-always-add-time-string
1756 (substring (current-time-string) 11 16))))) 1756 (substring (current-time-string) 11 16)))))
1757 (setq todos-date-from-calendar nil) 1757 (setq todo-date-from-calendar nil)
1758 (find-file-noselect file 'nowarn) 1758 (find-file-noselect file 'nowarn)
1759 (set-window-buffer (selected-window) 1759 (set-window-buffer (selected-window)
1760 (set-buffer (find-buffer-visiting file))) 1760 (set-buffer (find-buffer-visiting file)))
1761 ;; If this command was invoked outside of a Todos buffer, the 1761 ;; If this command was invoked outside of a Todo buffer, the
1762 ;; call to todos-current-category above returned nil. If we 1762 ;; call to todo-current-category above returned nil. If we
1763 ;; just entered Todos mode now, then cat was set to the file's 1763 ;; just entered Todo mode now, then cat was set to the file's
1764 ;; first category, but if todos-mode was already enabled, cat 1764 ;; first category, but if todo-mode was already enabled, cat
1765 ;; did not get set, so we have to set it explicitly. 1765 ;; did not get set, so we have to set it explicitly.
1766 (unless cat 1766 (unless cat
1767 (setq cat (todos-current-category))) 1767 (setq cat (todo-current-category)))
1768 (setq todos-current-todos-file file) 1768 (setq todo-current-todo-file file)
1769 (unless todos-global-current-todos-file 1769 (unless todo-global-current-todo-file
1770 (setq todos-global-current-todos-file todos-current-todos-file)) 1770 (setq todo-global-current-todo-file todo-current-todo-file))
1771 (let ((buffer-read-only nil) 1771 (let ((buffer-read-only nil)
1772 (called-from-outside (not (and todos-mm (equal cat ocat)))) 1772 (called-from-outside (not (and todo-mm (equal cat ocat))))
1773 done-only item-added) 1773 done-only item-added)
1774 (setq new-item 1774 (setq new-item
1775 ;; Add date, time and diary marking as required. 1775 ;; Add date, time and diary marking as required.
1776 (concat (if (not (and diary (not todos-include-in-diary))) 1776 (concat (if (not (and diary (not todo-include-in-diary)))
1777 todos-nondiary-start 1777 todo-nondiary-start
1778 (when (and nonmarking (not todos-diary-nonmarking)) 1778 (when (and nonmarking (not todo-diary-nonmarking))
1779 diary-nonmarking-symbol)) 1779 diary-nonmarking-symbol))
1780 date-string (when (and time-string ; Can be empty. 1780 date-string (when (and time-string ; Can be empty.
1781 (not (zerop (length 1781 (not (zerop (length
1782 time-string)))) 1782 time-string))))
1783 (concat " " time-string)) 1783 (concat " " time-string))
1784 (when (not (and diary (not todos-include-in-diary))) 1784 (when (not (and diary (not todo-include-in-diary)))
1785 todos-nondiary-end) 1785 todo-nondiary-end)
1786 " " new-item)) 1786 " " new-item))
1787 ;; Indent newlines inserted by C-q C-j if nonspace char follows. 1787 ;; Indent newlines inserted by C-q C-j if nonspace char follows.
1788 (setq new-item (replace-regexp-in-string "\\(\n\\)[^[:blank:]]" 1788 (setq new-item (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
@@ -1794,30 +1794,30 @@ the new item:
1794 ;; category is selected yet, or (ii) we invoked 1794 ;; category is selected yet, or (ii) we invoked
1795 ;; insertion "here" from outside the category we want 1795 ;; insertion "here" from outside the category we want
1796 ;; to insert in (with priority insertion, category 1796 ;; to insert in (with priority insertion, category
1797 ;; selection is done by todos-set-item-priority). 1797 ;; selection is done by todo-set-item-priority).
1798 (when (or (= (- (point-max) (point-min)) (buffer-size)) 1798 (when (or (= (- (point-max) (point-min)) (buffer-size))
1799 (and here called-from-outside)) 1799 (and here called-from-outside))
1800 (todos-category-number cat) 1800 (todo-category-number cat)
1801 (todos-category-select)) 1801 (todo-category-select))
1802 ;; If only done items are displayed in category, 1802 ;; If only done items are displayed in category,
1803 ;; toggle to todo items before inserting new item. 1803 ;; toggle to todo items before inserting new item.
1804 (when (save-excursion 1804 (when (save-excursion
1805 (goto-char (point-min)) 1805 (goto-char (point-min))
1806 (looking-at todos-done-string-start)) 1806 (looking-at todo-done-string-start))
1807 (setq done-only t) 1807 (setq done-only t)
1808 (todos-toggle-view-done-only)) 1808 (todo-toggle-view-done-only))
1809 (if here 1809 (if here
1810 (progn 1810 (progn
1811 ;; If command was invoked with point in done 1811 ;; If command was invoked with point in done
1812 ;; items section or outside of the current 1812 ;; items section or outside of the current
1813 ;; category, can't insert "here", so to be 1813 ;; category, can't insert "here", so to be
1814 ;; useful give new item top priority. 1814 ;; useful give new item top priority.
1815 (when (or (todos-done-item-section-p) 1815 (when (or (todo-done-item-section-p)
1816 called-from-outside 1816 called-from-outside
1817 done-only) 1817 done-only)
1818 (goto-char (point-min))) 1818 (goto-char (point-min)))
1819 (todos-insert-with-overlays new-item)) 1819 (todo-insert-with-overlays new-item))
1820 (todos-set-item-priority new-item cat t)) 1820 (todo-set-item-priority new-item cat t))
1821 (setq item-added t)) 1821 (setq item-added t))
1822 ;; If user cancels before setting priority, restore 1822 ;; If user cancels before setting priority, restore
1823 ;; display. 1823 ;; display.
@@ -1825,9 +1825,9 @@ the new item:
1825 (if ocat 1825 (if ocat
1826 (progn 1826 (progn
1827 (unless (equal cat ocat) 1827 (unless (equal cat ocat)
1828 (todos-category-number ocat) 1828 (todo-category-number ocat)
1829 (todos-category-select)) 1829 (todo-category-select))
1830 (and done-only (todos-toggle-view-done-only))) 1830 (and done-only (todo-toggle-view-done-only)))
1831 (set-window-buffer (selected-window) (set-buffer obuf))) 1831 (set-window-buffer (selected-window) (set-buffer obuf)))
1832 (goto-char opoint)) 1832 (goto-char opoint))
1833 ;; If the todo items section is not visible when the 1833 ;; If the todo items section is not visible when the
@@ -1839,16 +1839,16 @@ the new item:
1839 ;; out of view. So we recenter to make sure the todo 1839 ;; out of view. So we recenter to make sure the todo
1840 ;; items are displayed in the window. 1840 ;; items are displayed in the window.
1841 (when item-added (recenter))) 1841 (when item-added (recenter)))
1842 (todos-update-count 'todo 1) 1842 (todo-update-count 'todo 1)
1843 (if (or diary todos-include-in-diary) (todos-update-count 'diary 1)) 1843 (if (or diary todo-include-in-diary) (todo-update-count 'diary 1))
1844 (todos-update-categories-sexp)))))) 1844 (todo-update-categories-sexp))))))
1845 1845
1846(defun todos-set-date-from-calendar () 1846(defun todo-set-date-from-calendar ()
1847 "Return string of date chosen from Calendar." 1847 "Return string of date chosen from Calendar."
1848 (cond ((and (stringp todos-date-from-calendar) 1848 (cond ((and (stringp todo-date-from-calendar)
1849 (string-match todos-date-pattern todos-date-from-calendar)) 1849 (string-match todo-date-pattern todo-date-from-calendar))
1850 todos-date-from-calendar) 1850 todo-date-from-calendar)
1851 (todos-date-from-calendar 1851 (todo-date-from-calendar
1852 (let (calendar-view-diary-initially-flag) 1852 (let (calendar-view-diary-initially-flag)
1853 (calendar)) ; *Calendar* is now current buffer. 1853 (calendar)) ; *Calendar* is now current buffer.
1854 (define-key calendar-mode-map [remap newline] 'exit-recursive-edit) 1854 (define-key calendar-mode-map [remap newline] 'exit-recursive-edit)
@@ -1863,121 +1863,121 @@ the new item:
1863 (recursive-edit) 1863 (recursive-edit)
1864 (unwind-protect 1864 (unwind-protect
1865 (when (equal (buffer-name) calendar-buffer) 1865 (when (equal (buffer-name) calendar-buffer)
1866 (setq todos-date-from-calendar 1866 (setq todo-date-from-calendar
1867 (calendar-date-string (calendar-cursor-to-date t) t t)) 1867 (calendar-date-string (calendar-cursor-to-date t) t t))
1868 (calendar-exit) 1868 (calendar-exit)
1869 todos-date-from-calendar) 1869 todo-date-from-calendar)
1870 (define-key calendar-mode-map [remap newline] nil) 1870 (define-key calendar-mode-map [remap newline] nil)
1871 (define-key calendar-mode-map [remap calendar-exit] nil) 1871 (define-key calendar-mode-map [remap calendar-exit] nil)
1872 (unless (zerop (recursion-depth)) (exit-recursive-edit)) 1872 (unless (zerop (recursion-depth)) (exit-recursive-edit))
1873 (when (stringp todos-date-from-calendar) 1873 (when (stringp todo-date-from-calendar)
1874 todos-date-from-calendar))))) 1874 todo-date-from-calendar)))))
1875 1875
1876(defun todos-insert-item-from-calendar (&optional arg) 1876(defun todo-insert-item-from-calendar (&optional arg)
1877 "Prompt for and insert a new item with date selected from calendar. 1877 "Prompt for and insert a new item with date selected from calendar.
1878Invoked without prefix argument ARG, insert the item into the 1878Invoked without prefix argument ARG, insert the item into the
1879current category, without one prefix argument, prompt for the 1879current category, without one prefix argument, prompt for the
1880category from the current todo file or from one listed in 1880category from the current todo file or from one listed in
1881`todos-category-completions-files'; with two prefix arguments, 1881`todo-category-completions-files'; with two prefix arguments,
1882prompt for a todo file and then for a category in it." 1882prompt for a todo file and then for a category in it."
1883 (interactive "P") 1883 (interactive "P")
1884 (setq todos-date-from-calendar 1884 (setq todo-date-from-calendar
1885 (calendar-date-string (calendar-cursor-to-date t) t t)) 1885 (calendar-date-string (calendar-cursor-to-date t) t t))
1886 (calendar-exit) 1886 (calendar-exit)
1887 (todos-basic-insert-item arg nil nil todos-date-from-calendar)) 1887 (todo-basic-insert-item arg nil nil todo-date-from-calendar))
1888 1888
1889(define-key calendar-mode-map "it" 'todos-insert-item-from-calendar) 1889(define-key calendar-mode-map "it" 'todo-insert-item-from-calendar)
1890 1890
1891(defun todos-copy-item () 1891(defun todo-copy-item ()
1892 "Copy item at point and insert the copy as a new item." 1892 "Copy item at point and insert the copy as a new item."
1893 (interactive) 1893 (interactive)
1894 (unless (or (todos-done-item-p) (looking-at "^$")) 1894 (unless (or (todo-done-item-p) (looking-at "^$"))
1895 (let ((copy (todos-item-string)) 1895 (let ((copy (todo-item-string))
1896 (diary-item (todos-diary-item-p))) 1896 (diary-item (todo-diary-item-p)))
1897 (todos-set-item-priority copy (todos-current-category) t) 1897 (todo-set-item-priority copy (todo-current-category) t)
1898 (todos-update-count 'todo 1) 1898 (todo-update-count 'todo 1)
1899 (when diary-item (todos-update-count 'diary 1)) 1899 (when diary-item (todo-update-count 'diary 1))
1900 (todos-update-categories-sexp)))) 1900 (todo-update-categories-sexp))))
1901 1901
1902(defun todos-delete-item () 1902(defun todo-delete-item ()
1903 "Delete at least one item in this category. 1903 "Delete at least one item in this category.
1904If there are marked items, delete all of these; otherwise, delete 1904If there are marked items, delete all of these; otherwise, delete
1905the item at point." 1905the item at point."
1906 (interactive) 1906 (interactive)
1907 (let (ov) 1907 (let (ov)
1908 (unwind-protect 1908 (unwind-protect
1909 (let* ((cat (todos-current-category)) 1909 (let* ((cat (todo-current-category))
1910 (marked (assoc cat todos-categories-with-marks)) 1910 (marked (assoc cat todo-categories-with-marks))
1911 (item (unless marked (todos-item-string))) 1911 (item (unless marked (todo-item-string)))
1912 (answer (if marked 1912 (answer (if marked
1913 (todos-y-or-n-p 1913 (todo-y-or-n-p
1914 "Permanently delete all marked items? ") 1914 "Permanently delete all marked items? ")
1915 (when item 1915 (when item
1916 (setq ov (make-overlay 1916 (setq ov (make-overlay
1917 (save-excursion (todos-item-start)) 1917 (save-excursion (todo-item-start))
1918 (save-excursion (todos-item-end)))) 1918 (save-excursion (todo-item-end))))
1919 (overlay-put ov 'face 'todos-search) 1919 (overlay-put ov 'face 'todo-search)
1920 (todos-y-or-n-p "Permanently delete this item? ")))) 1920 (todo-y-or-n-p "Permanently delete this item? "))))
1921 buffer-read-only) 1921 buffer-read-only)
1922 (when answer 1922 (when answer
1923 (and marked (goto-char (point-min))) 1923 (and marked (goto-char (point-min)))
1924 (catch 'done 1924 (catch 'done
1925 (while (not (eobp)) 1925 (while (not (eobp))
1926 (if (or (and marked (todos-marked-item-p)) item) 1926 (if (or (and marked (todo-marked-item-p)) item)
1927 (progn 1927 (progn
1928 (if (todos-done-item-p) 1928 (if (todo-done-item-p)
1929 (todos-update-count 'done -1) 1929 (todo-update-count 'done -1)
1930 (todos-update-count 'todo -1 cat) 1930 (todo-update-count 'todo -1 cat)
1931 (and (todos-diary-item-p) 1931 (and (todo-diary-item-p)
1932 (todos-update-count 'diary -1))) 1932 (todo-update-count 'diary -1)))
1933 (if ov (delete-overlay ov)) 1933 (if ov (delete-overlay ov))
1934 (todos-remove-item) 1934 (todo-remove-item)
1935 ;; Don't leave point below last item. 1935 ;; Don't leave point below last item.
1936 (and item (bolp) (eolp) (< (point-min) (point-max)) 1936 (and item (bolp) (eolp) (< (point-min) (point-max))
1937 (todos-backward-item)) 1937 (todo-backward-item))
1938 (when item 1938 (when item
1939 (throw 'done (setq item nil)))) 1939 (throw 'done (setq item nil))))
1940 (todos-forward-item)))) 1940 (todo-forward-item))))
1941 (when marked 1941 (when marked
1942 (setq todos-categories-with-marks 1942 (setq todo-categories-with-marks
1943 (assq-delete-all cat todos-categories-with-marks))) 1943 (assq-delete-all cat todo-categories-with-marks)))
1944 (todos-update-categories-sexp) 1944 (todo-update-categories-sexp)
1945 (todos-prefix-overlays))) 1945 (todo-prefix-overlays)))
1946 (if ov (delete-overlay ov))))) 1946 (if ov (delete-overlay ov)))))
1947 1947
1948(defun todos-edit-item (&optional arg) 1948(defun todo-edit-item (&optional arg)
1949 "Edit the Todo item at point. 1949 "Edit the Todo item at point.
1950With non-nil prefix argument ARG, include the item's date/time 1950With non-nil prefix argument ARG, include the item's date/time
1951header, making it also editable; otherwise, include only the item 1951header, making it also editable; otherwise, include only the item
1952content. 1952content.
1953 1953
1954If the item consists of only one logical line, edit it in the 1954If the item consists of only one logical line, edit it in the
1955minibuffer; otherwise, edit it in Todos Edit mode." 1955minibuffer; otherwise, edit it in Todo Edit mode."
1956 (interactive "P") 1956 (interactive "P")
1957 (when (todos-item-string) 1957 (when (todo-item-string)
1958 (let* ((opoint (point)) 1958 (let* ((opoint (point))
1959 (start (todos-item-start)) 1959 (start (todo-item-start))
1960 (item-beg (progn 1960 (item-beg (progn
1961 (re-search-forward 1961 (re-search-forward
1962 (concat todos-date-string-start todos-date-pattern 1962 (concat todo-date-string-start todo-date-pattern
1963 "\\( " diary-time-regexp "\\)?" 1963 "\\( " diary-time-regexp "\\)?"
1964 (regexp-quote todos-nondiary-end) "?") 1964 (regexp-quote todo-nondiary-end) "?")
1965 (line-end-position) t) 1965 (line-end-position) t)
1966 (1+ (- (point) start)))) 1966 (1+ (- (point) start))))
1967 (header (substring (todos-item-string) 0 item-beg)) 1967 (header (substring (todo-item-string) 0 item-beg))
1968 (item (if arg (todos-item-string) 1968 (item (if arg (todo-item-string)
1969 (substring (todos-item-string) item-beg))) 1969 (substring (todo-item-string) item-beg)))
1970 (multiline (> (length (split-string item "\n")) 1)) 1970 (multiline (> (length (split-string item "\n")) 1))
1971 (buffer-read-only nil)) 1971 (buffer-read-only nil))
1972 (if multiline 1972 (if multiline
1973 (todos-edit-multiline-item) 1973 (todo-edit-multiline-item)
1974 (let ((new (concat (if arg "" header) 1974 (let ((new (concat (if arg "" header)
1975 (read-string "Edit: " (if arg 1975 (read-string "Edit: " (if arg
1976 (cons item item-beg) 1976 (cons item item-beg)
1977 (cons item 0)))))) 1977 (cons item 0))))))
1978 (when arg 1978 (when arg
1979 (while (not (string-match (concat todos-date-string-start 1979 (while (not (string-match (concat todo-date-string-start
1980 todos-date-pattern) new)) 1980 todo-date-pattern) new))
1981 (setq new (read-from-minibuffer 1981 (setq new (read-from-minibuffer
1982 "Item must start with a date: " new)))) 1982 "Item must start with a date: " new))))
1983 ;; Ensure lines following hard newlines are indented. 1983 ;; Ensure lines following hard newlines are indented.
@@ -1985,34 +1985,34 @@ minibuffer; otherwise, edit it in Todos Edit mode."
1985 "\n\t" new nil nil 1)) 1985 "\n\t" new nil nil 1))
1986 ;; If user moved point during editing, make sure it moves back. 1986 ;; If user moved point during editing, make sure it moves back.
1987 (goto-char opoint) 1987 (goto-char opoint)
1988 (todos-remove-item) 1988 (todo-remove-item)
1989 (todos-insert-with-overlays new) 1989 (todo-insert-with-overlays new)
1990 (move-to-column item-beg)))))) 1990 (move-to-column item-beg))))))
1991 1991
1992(defun todos-edit-multiline-item () 1992(defun todo-edit-multiline-item ()
1993 "Edit current Todo item in Todos Edit mode. 1993 "Edit current Todo item in Todo Edit mode.
1994Use of newlines invokes `todos-indent' to insure compliance with 1994Use of newlines invokes `todo-indent' to insure compliance with
1995the format of Diary entries." 1995the format of Diary entries."
1996 (interactive) 1996 (interactive)
1997 (when (todos-item-string) 1997 (when (todo-item-string)
1998 (let ((buf todos-edit-buffer)) 1998 (let ((buf todo-edit-buffer))
1999 (set-window-buffer (selected-window) 1999 (set-window-buffer (selected-window)
2000 (set-buffer (make-indirect-buffer (buffer-name) buf))) 2000 (set-buffer (make-indirect-buffer (buffer-name) buf)))
2001 (narrow-to-region (todos-item-start) (todos-item-end)) 2001 (narrow-to-region (todo-item-start) (todo-item-end))
2002 (todos-edit-mode) 2002 (todo-edit-mode)
2003 (message "%s" (substitute-command-keys 2003 (message "%s" (substitute-command-keys
2004 (concat "Type \\[todos-edit-quit] " 2004 (concat "Type \\[todo-edit-quit] "
2005 "to return to Todos mode.\n")))))) 2005 "to return to Todo mode.\n"))))))
2006 2006
2007(defun todos-edit-quit () 2007(defun todo-edit-quit ()
2008 "Return from Todos Edit mode to Todos mode. 2008 "Return from Todo Edit mode to Todo mode.
2009If the item contains hard line breaks, make sure the following 2009If the item contains hard line breaks, make sure the following
2010lines are indented by `todos-indent-to-here' to conform to diary 2010lines are indented by `todo-indent-to-here' to conform to diary
2011format. 2011format.
2012 2012
2013If the whole file was in Todos Edit mode, check before returning 2013If the whole file was in Todo Edit mode, check before returning
2014whether the file is still a valid Todos file and if so, also 2014whether the file is still a valid Todo file and if so, also
2015recalculate the Todos categories sexp, in case changes were made 2015recalculate the Todo categories sexp, in case changes were made
2016in the number or names of categories." 2016in the number or names of categories."
2017 (interactive) 2017 (interactive)
2018 (if (> (buffer-size) (- (point-max) (point-min))) 2018 (if (> (buffer-size) (- (point-max) (point-min)))
@@ -2020,8 +2020,8 @@ in the number or names of categories."
2020 (let ((item (buffer-string)) 2020 (let ((item (buffer-string))
2021 (regex "\\(\n\\)[^[:blank:]]") 2021 (regex "\\(\n\\)[^[:blank:]]")
2022 (buf (buffer-base-buffer))) 2022 (buf (buffer-base-buffer)))
2023 (while (not (string-match (concat todos-date-string-start 2023 (while (not (string-match (concat todo-date-string-start
2024 todos-date-pattern) item)) 2024 todo-date-pattern) item))
2025 (setq item (read-from-minibuffer 2025 (setq item (read-from-minibuffer
2026 "Item must start with a date: " item))) 2026 "Item must start with a date: " item)))
2027 ;; Ensure lines following hard newlines are indented. 2027 ;; Ensure lines following hard newlines are indented.
@@ -2033,16 +2033,16 @@ in the number or names of categories."
2033 (unless (eq (current-buffer) buf) 2033 (unless (eq (current-buffer) buf)
2034 (set-window-buffer (selected-window) (set-buffer buf)))) 2034 (set-window-buffer (selected-window) (set-buffer buf))))
2035 ;; We got here via `F e'. 2035 ;; We got here via `F e'.
2036 (when (todos-check-format) 2036 (when (todo-check-format)
2037 ;; FIXME: separate out sexp check? 2037 ;; FIXME: separate out sexp check?
2038 ;; If manual editing makes e.g. item counts change, have to 2038 ;; If manual editing makes e.g. item counts change, have to
2039 ;; call this to update todos-categories, but it restores 2039 ;; call this to update todo-categories, but it restores
2040 ;; category order to list order. 2040 ;; category order to list order.
2041 ;; (todos-repair-categories-sexp) 2041 ;; (todo-repair-categories-sexp)
2042 ;; Compare (todos-make-categories-list t) with sexp and if 2042 ;; Compare (todo-make-categories-list t) with sexp and if
2043 ;; different ask (todos-update-categories-sexp) ? 2043 ;; different ask (todo-update-categories-sexp) ?
2044 (todos-mode) 2044 (todo-mode)
2045 (let* ((cat-beg (concat "^" (regexp-quote todos-category-beg) 2045 (let* ((cat-beg (concat "^" (regexp-quote todo-category-beg)
2046 "\\(.*\\)$")) 2046 "\\(.*\\)$"))
2047 (curline (buffer-substring-no-properties 2047 (curline (buffer-substring-no-properties
2048 (line-beginning-position) (line-end-position))) 2048 (line-beginning-position) (line-end-position)))
@@ -2051,11 +2051,11 @@ in the number or names of categories."
2051 ((or (re-search-backward cat-beg nil t) 2051 ((or (re-search-backward cat-beg nil t)
2052 (re-search-forward cat-beg nil t)) 2052 (re-search-forward cat-beg nil t))
2053 (match-string-no-properties 1))))) 2053 (match-string-no-properties 1)))))
2054 (todos-category-number cat) 2054 (todo-category-number cat)
2055 (todos-category-select) 2055 (todo-category-select)
2056 (goto-char (point-min)))))) 2056 (goto-char (point-min))))))
2057 2057
2058(defun todos-basic-edit-item-header (what &optional inc) 2058(defun todo-basic-edit-item-header (what &optional inc)
2059 "Function underlying commands to edit item date/time header. 2059 "Function underlying commands to edit item date/time header.
2060 2060
2061The argument WHAT (passed by invoking commands) specifies what 2061The argument WHAT (passed by invoking commands) specifies what
@@ -2080,25 +2080,25 @@ string components as necessary.
2080 2080
2081If there are marked items, apply the same edit to all of these; 2081If there are marked items, apply the same edit to all of these;
2082otherwise, edit just the item at point." 2082otherwise, edit just the item at point."
2083 (let* ((cat (todos-current-category)) 2083 (let* ((cat (todo-current-category))
2084 (marked (assoc cat todos-categories-with-marks)) 2084 (marked (assoc cat todo-categories-with-marks))
2085 (first t) 2085 (first t)
2086 (todos-date-from-calendar t) 2086 (todo-date-from-calendar t)
2087 (buffer-read-only nil) 2087 (buffer-read-only nil)
2088 ndate ntime year monthname month day 2088 ndate ntime year monthname month day
2089 dayname) ; Needed by calendar-date-display-form. 2089 dayname) ; Needed by calendar-date-display-form.
2090 (save-excursion 2090 (save-excursion
2091 (or (and marked (goto-char (point-min))) (todos-item-start)) 2091 (or (and marked (goto-char (point-min))) (todo-item-start))
2092 (catch 'end 2092 (catch 'end
2093 (while (not (eobp)) 2093 (while (not (eobp))
2094 (and marked 2094 (and marked
2095 (while (not (todos-marked-item-p)) 2095 (while (not (todo-marked-item-p))
2096 (todos-forward-item) 2096 (todo-forward-item)
2097 (and (eobp) (throw 'end nil)))) 2097 (and (eobp) (throw 'end nil))))
2098 (re-search-forward (concat todos-date-string-start "\\(?1:" 2098 (re-search-forward (concat todo-date-string-start "\\(?1:"
2099 todos-date-pattern 2099 todo-date-pattern
2100 "\\)\\(?2: " diary-time-regexp "\\)?" 2100 "\\)\\(?2: " diary-time-regexp "\\)?"
2101 (regexp-quote todos-nondiary-end) "?") 2101 (regexp-quote todo-nondiary-end) "?")
2102 (line-end-position) t) 2102 (line-end-position) t)
2103 (let* ((odate (match-string-no-properties 1)) 2103 (let* ((odate (match-string-no-properties 1))
2104 (otime (match-string-no-properties 2)) 2104 (otime (match-string-no-properties 2))
@@ -2107,9 +2107,9 @@ otherwise, edit just the item at point."
2107 (omonth (match-string-no-properties 7)) 2107 (omonth (match-string-no-properties 7))
2108 (oday (match-string-no-properties 8)) 2108 (oday (match-string-no-properties 8))
2109 (oyear (match-string-no-properties 9)) 2109 (oyear (match-string-no-properties 9))
2110 (tmn-array todos-month-name-array) 2110 (tmn-array todo-month-name-array)
2111 (mlist (append tmn-array nil)) 2111 (mlist (append tmn-array nil))
2112 (tma-array todos-month-abbrev-array) 2112 (tma-array todo-month-abbrev-array)
2113 (mablist (append tma-array nil)) 2113 (mablist (append tma-array nil))
2114 (yy (and oyear (unless (string= oyear "*") 2114 (yy (and oyear (unless (string= oyear "*")
2115 (string-to-number oyear)))) 2115 (string-to-number oyear))))
@@ -2125,15 +2125,15 @@ otherwise, edit just the item at point."
2125 (when first 2125 (when first
2126 (cond 2126 (cond
2127 ((eq what 'date) 2127 ((eq what 'date)
2128 (setq ndate (todos-read-date))) 2128 (setq ndate (todo-read-date)))
2129 ((eq what 'calendar) 2129 ((eq what 'calendar)
2130 (setq ndate (save-match-data (todos-set-date-from-calendar)))) 2130 (setq ndate (save-match-data (todo-set-date-from-calendar))))
2131 ((eq what 'today) 2131 ((eq what 'today)
2132 (setq ndate (calendar-date-string (calendar-current-date) t t))) 2132 (setq ndate (calendar-date-string (calendar-current-date) t t)))
2133 ((eq what 'dayname) 2133 ((eq what 'dayname)
2134 (setq ndate (todos-read-dayname))) 2134 (setq ndate (todo-read-dayname)))
2135 ((eq what 'time) 2135 ((eq what 'time)
2136 (setq ntime (save-match-data (todos-read-time))) 2136 (setq ntime (save-match-data (todo-read-time)))
2137 (when (> (length ntime) 0) 2137 (when (> (length ntime) 0)
2138 (setq ntime (concat " " ntime)))) 2138 (setq ntime (concat " " ntime))))
2139 ;; When date string consists only of a day name, 2139 ;; When date string consists only of a day name,
@@ -2144,7 +2144,7 @@ otherwise, edit just the item at point."
2144 monthname omonthname 2144 monthname omonthname
2145 month omonth 2145 month omonth
2146 year (cond ((not current-prefix-arg) 2146 year (cond ((not current-prefix-arg)
2147 (todos-read-date 'year)) 2147 (todo-read-date 'year))
2148 ((string= oyear "*") 2148 ((string= oyear "*")
2149 (user-error "Cannot increment *")) 2149 (user-error "Cannot increment *"))
2150 (t 2150 (t
@@ -2156,7 +2156,7 @@ otherwise, edit just the item at point."
2156 month 2156 month
2157 monthname) 2157 monthname)
2158 (cond ((not current-prefix-arg) 2158 (cond ((not current-prefix-arg)
2159 (todos-read-date 'month)) 2159 (todo-read-date 'month))
2160 ((or (string= omonth "*") (= mm 13)) 2160 ((or (string= omonth "*") (= mm 13))
2161 (user-error "Cannot increment *")) 2161 (user-error "Cannot increment *"))
2162 (t 2162 (t
@@ -2196,7 +2196,7 @@ otherwise, edit just the item at point."
2196 monthname omonthname 2196 monthname omonthname
2197 day (cond 2197 day (cond
2198 ((not current-prefix-arg) 2198 ((not current-prefix-arg)
2199 (todos-read-date 'day mm oyear)) 2199 (todo-read-date 'day mm oyear))
2200 ((string= oday "*") 2200 ((string= oday "*")
2201 (user-error "Cannot increment *")) 2201 (user-error "Cannot increment *"))
2202 ((or (string= omonth "*") (string= omonthname "*")) 2202 ((or (string= omonth "*") (string= omonthname "*"))
@@ -2235,68 +2235,68 @@ otherwise, edit just the item at point."
2235 (replace-match ntime nil nil nil 2) 2235 (replace-match ntime nil nil nil 2)
2236 (goto-char (match-end 1)) 2236 (goto-char (match-end 1))
2237 (insert ntime))) 2237 (insert ntime)))
2238 (setq todos-date-from-calendar nil) 2238 (setq todo-date-from-calendar nil)
2239 (setq first nil)) 2239 (setq first nil))
2240 ;; Apply the changes to the first marked item header to the 2240 ;; Apply the changes to the first marked item header to the
2241 ;; remaining marked items. If there are no marked items, 2241 ;; remaining marked items. If there are no marked items,
2242 ;; we're finished. 2242 ;; we're finished.
2243 (if marked 2243 (if marked
2244 (todos-forward-item) 2244 (todo-forward-item)
2245 (goto-char (point-max)))))))) 2245 (goto-char (point-max))))))))
2246 2246
2247(defun todos-edit-item-header () 2247(defun todo-edit-item-header ()
2248 "Interactively edit at least the date of item's date/time header. 2248 "Interactively edit at least the date of item's date/time header.
2249If user option `todos-always-add-time-string' is non-nil, also 2249If user option `todo-always-add-time-string' is non-nil, also
2250edit item's time string." 2250edit item's time string."
2251 (interactive) 2251 (interactive)
2252 (todos-basic-edit-item-header 'date) 2252 (todo-basic-edit-item-header 'date)
2253 (when todos-always-add-time-string 2253 (when todo-always-add-time-string
2254 (todos-edit-item-time))) 2254 (todo-edit-item-time)))
2255 2255
2256(defun todos-edit-item-time () 2256(defun todo-edit-item-time ()
2257 "Interactively edit the time string of item's date/time header." 2257 "Interactively edit the time string of item's date/time header."
2258 (interactive) 2258 (interactive)
2259 (todos-basic-edit-item-header 'time)) 2259 (todo-basic-edit-item-header 'time))
2260 2260
2261(defun todos-edit-item-date-from-calendar () 2261(defun todo-edit-item-date-from-calendar ()
2262 "Interactively edit item's date using the Calendar." 2262 "Interactively edit item's date using the Calendar."
2263 (interactive) 2263 (interactive)
2264 (todos-basic-edit-item-header 'calendar)) 2264 (todo-basic-edit-item-header 'calendar))
2265 2265
2266(defun todos-edit-item-date-to-today () 2266(defun todo-edit-item-date-to-today ()
2267 "Set item's date to today's date." 2267 "Set item's date to today's date."
2268 (interactive) 2268 (interactive)
2269 (todos-basic-edit-item-header 'today)) 2269 (todo-basic-edit-item-header 'today))
2270 2270
2271(defun todos-edit-item-date-day-name () 2271(defun todo-edit-item-date-day-name ()
2272 "Replace item's date with the name of a day of the week." 2272 "Replace item's date with the name of a day of the week."
2273 (interactive) 2273 (interactive)
2274 (todos-basic-edit-item-header 'dayname)) 2274 (todo-basic-edit-item-header 'dayname))
2275 2275
2276(defun todos-edit-item-date-year (&optional inc) 2276(defun todo-edit-item-date-year (&optional inc)
2277 "Interactively edit the year of item's date string. 2277 "Interactively edit the year of item's date string.
2278With prefix argument INC a positive or negative integer, 2278With prefix argument INC a positive or negative integer,
2279increment or decrement the year by INC." 2279increment or decrement the year by INC."
2280 (interactive "p") 2280 (interactive "p")
2281 (todos-basic-edit-item-header 'year inc)) 2281 (todo-basic-edit-item-header 'year inc))
2282 2282
2283(defun todos-edit-item-date-month (&optional inc) 2283(defun todo-edit-item-date-month (&optional inc)
2284 "Interactively edit the month of item's date string. 2284 "Interactively edit the month of item's date string.
2285With prefix argument INC a positive or negative integer, 2285With prefix argument INC a positive or negative integer,
2286increment or decrement the month by INC." 2286increment or decrement the month by INC."
2287 (interactive "p") 2287 (interactive "p")
2288 (todos-basic-edit-item-header 'month inc)) 2288 (todo-basic-edit-item-header 'month inc))
2289 2289
2290(defun todos-edit-item-date-day (&optional inc) 2290(defun todo-edit-item-date-day (&optional inc)
2291 "Interactively edit the day of the month of item's date string. 2291 "Interactively edit the day of the month of item's date string.
2292With prefix argument INC a positive or negative integer, 2292With prefix argument INC a positive or negative integer,
2293increment or decrement the day by INC." 2293increment or decrement the day by INC."
2294 (interactive "p") 2294 (interactive "p")
2295 (todos-basic-edit-item-header 'day inc)) 2295 (todo-basic-edit-item-header 'day inc))
2296 2296
2297(defun todos-edit-item-diary-inclusion () 2297(defun todo-edit-item-diary-inclusion ()
2298 "Change diary status of one or more todo items in this category. 2298 "Change diary status of one or more todo items in this category.
2299That is, insert `todos-nondiary-marker' if the candidate items 2299That is, insert `todo-nondiary-marker' if the candidate items
2300lack this marking; otherwise, remove it. 2300lack this marking; otherwise, remove it.
2301 2301
2302If there are marked todo items, change the diary status of all 2302If there are marked todo items, change the diary status of all
@@ -2304,71 +2304,71 @@ and only these, otherwise change the diary status of the item at
2304point." 2304point."
2305 (interactive) 2305 (interactive)
2306 (let ((buffer-read-only) 2306 (let ((buffer-read-only)
2307 (marked (assoc (todos-current-category) 2307 (marked (assoc (todo-current-category)
2308 todos-categories-with-marks))) 2308 todo-categories-with-marks)))
2309 (catch 'stop 2309 (catch 'stop
2310 (save-excursion 2310 (save-excursion
2311 (when marked (goto-char (point-min))) 2311 (when marked (goto-char (point-min)))
2312 (while (not (eobp)) 2312 (while (not (eobp))
2313 (if (todos-done-item-p) 2313 (if (todo-done-item-p)
2314 (throw 'stop (message "Done items cannot be edited")) 2314 (throw 'stop (message "Done items cannot be edited"))
2315 (unless (and marked (not (todos-marked-item-p))) 2315 (unless (and marked (not (todo-marked-item-p)))
2316 (let* ((beg (todos-item-start)) 2316 (let* ((beg (todo-item-start))
2317 (lim (save-excursion (todos-item-end))) 2317 (lim (save-excursion (todo-item-end)))
2318 (end (save-excursion 2318 (end (save-excursion
2319 (or (todos-time-string-matcher lim) 2319 (or (todo-time-string-matcher lim)
2320 (todos-date-string-matcher lim))))) 2320 (todo-date-string-matcher lim)))))
2321 (if (looking-at (regexp-quote todos-nondiary-start)) 2321 (if (looking-at (regexp-quote todo-nondiary-start))
2322 (progn 2322 (progn
2323 (replace-match "") 2323 (replace-match "")
2324 (search-forward todos-nondiary-end (1+ end) t) 2324 (search-forward todo-nondiary-end (1+ end) t)
2325 (replace-match "") 2325 (replace-match "")
2326 (todos-update-count 'diary 1)) 2326 (todo-update-count 'diary 1))
2327 (when end 2327 (when end
2328 (insert todos-nondiary-start) 2328 (insert todo-nondiary-start)
2329 (goto-char (1+ end)) 2329 (goto-char (1+ end))
2330 (insert todos-nondiary-end) 2330 (insert todo-nondiary-end)
2331 (todos-update-count 'diary -1))))) 2331 (todo-update-count 'diary -1)))))
2332 (unless marked (throw 'stop nil)) 2332 (unless marked (throw 'stop nil))
2333 (todos-forward-item))))) 2333 (todo-forward-item)))))
2334 (todos-update-categories-sexp))) 2334 (todo-update-categories-sexp)))
2335 2335
2336(defun todos-edit-category-diary-inclusion (arg) 2336(defun todo-edit-category-diary-inclusion (arg)
2337 "Make all items in this category diary items. 2337 "Make all items in this category diary items.
2338With prefix ARG, make all items in this category non-diary 2338With prefix ARG, make all items in this category non-diary
2339items." 2339items."
2340 (interactive "P") 2340 (interactive "P")
2341 (save-excursion 2341 (save-excursion
2342 (goto-char (point-min)) 2342 (goto-char (point-min))
2343 (let ((todo-count (todos-get-count 'todo)) 2343 (let ((todo-count (todo-get-count 'todo))
2344 (diary-count (todos-get-count 'diary)) 2344 (diary-count (todo-get-count 'diary))
2345 (buffer-read-only)) 2345 (buffer-read-only))
2346 (catch 'stop 2346 (catch 'stop
2347 (while (not (eobp)) 2347 (while (not (eobp))
2348 (if (todos-done-item-p) ; We've gone too far. 2348 (if (todo-done-item-p) ; We've gone too far.
2349 (throw 'stop nil) 2349 (throw 'stop nil)
2350 (let* ((beg (todos-item-start)) 2350 (let* ((beg (todo-item-start))
2351 (lim (save-excursion (todos-item-end))) 2351 (lim (save-excursion (todo-item-end)))
2352 (end (save-excursion 2352 (end (save-excursion
2353 (or (todos-time-string-matcher lim) 2353 (or (todo-time-string-matcher lim)
2354 (todos-date-string-matcher lim))))) 2354 (todo-date-string-matcher lim)))))
2355 (if arg 2355 (if arg
2356 (unless (looking-at (regexp-quote todos-nondiary-start)) 2356 (unless (looking-at (regexp-quote todo-nondiary-start))
2357 (insert todos-nondiary-start) 2357 (insert todo-nondiary-start)
2358 (goto-char (1+ end)) 2358 (goto-char (1+ end))
2359 (insert todos-nondiary-end)) 2359 (insert todo-nondiary-end))
2360 (when (looking-at (regexp-quote todos-nondiary-start)) 2360 (when (looking-at (regexp-quote todo-nondiary-start))
2361 (replace-match "") 2361 (replace-match "")
2362 (search-forward todos-nondiary-end (1+ end) t) 2362 (search-forward todo-nondiary-end (1+ end) t)
2363 (replace-match ""))))) 2363 (replace-match "")))))
2364 (todos-forward-item)) 2364 (todo-forward-item))
2365 (unless (if arg (zerop diary-count) (= diary-count todo-count)) 2365 (unless (if arg (zerop diary-count) (= diary-count todo-count))
2366 (todos-update-count 'diary (if arg 2366 (todo-update-count 'diary (if arg
2367 (- diary-count) 2367 (- diary-count)
2368 (- todo-count diary-count)))) 2368 (- todo-count diary-count))))
2369 (todos-update-categories-sexp))))) 2369 (todo-update-categories-sexp)))))
2370 2370
2371(defun todos-edit-item-diary-nonmarking () 2371(defun todo-edit-item-diary-nonmarking ()
2372 "Change non-marking of one or more diary items in this category. 2372 "Change non-marking of one or more diary items in this category.
2373That is, insert `diary-nonmarking-symbol' if the candidate items 2373That is, insert `diary-nonmarking-symbol' if the candidate items
2374lack this marking; otherwise, remove it. 2374lack this marking; otherwise, remove it.
@@ -2378,24 +2378,24 @@ all and only these, otherwise change the non-marking status of
2378the item at point." 2378the item at point."
2379 (interactive) 2379 (interactive)
2380 (let ((buffer-read-only) 2380 (let ((buffer-read-only)
2381 (marked (assoc (todos-current-category) 2381 (marked (assoc (todo-current-category)
2382 todos-categories-with-marks))) 2382 todo-categories-with-marks)))
2383 (catch 'stop 2383 (catch 'stop
2384 (save-excursion 2384 (save-excursion
2385 (when marked (goto-char (point-min))) 2385 (when marked (goto-char (point-min)))
2386 (while (not (eobp)) 2386 (while (not (eobp))
2387 (if (todos-done-item-p) 2387 (if (todo-done-item-p)
2388 (throw 'stop (message "Done items cannot be edited")) 2388 (throw 'stop (message "Done items cannot be edited"))
2389 (unless (and marked (not (todos-marked-item-p))) 2389 (unless (and marked (not (todo-marked-item-p)))
2390 (todos-item-start) 2390 (todo-item-start)
2391 (unless (looking-at (regexp-quote todos-nondiary-start)) 2391 (unless (looking-at (regexp-quote todo-nondiary-start))
2392 (if (looking-at (regexp-quote diary-nonmarking-symbol)) 2392 (if (looking-at (regexp-quote diary-nonmarking-symbol))
2393 (replace-match "") 2393 (replace-match "")
2394 (insert diary-nonmarking-symbol)))) 2394 (insert diary-nonmarking-symbol))))
2395 (unless marked (throw 'stop nil)) 2395 (unless marked (throw 'stop nil))
2396 (todos-forward-item))))))) 2396 (todo-forward-item)))))))
2397 2397
2398(defun todos-edit-category-diary-nonmarking (arg) 2398(defun todo-edit-category-diary-nonmarking (arg)
2399 "Add `diary-nonmarking-symbol' to all diary items in this category. 2399 "Add `diary-nonmarking-symbol' to all diary items in this category.
2400With prefix ARG, remove `diary-nonmarking-symbol' from all diary 2400With prefix ARG, remove `diary-nonmarking-symbol' from all diary
2401items in this category." 2401items in this category."
@@ -2405,17 +2405,17 @@ items in this category."
2405 (let (buffer-read-only) 2405 (let (buffer-read-only)
2406 (catch 'stop 2406 (catch 'stop
2407 (while (not (eobp)) 2407 (while (not (eobp))
2408 (if (todos-done-item-p) ; We've gone too far. 2408 (if (todo-done-item-p) ; We've gone too far.
2409 (throw 'stop nil) 2409 (throw 'stop nil)
2410 (unless (looking-at (regexp-quote todos-nondiary-start)) 2410 (unless (looking-at (regexp-quote todo-nondiary-start))
2411 (if arg 2411 (if arg
2412 (when (looking-at (regexp-quote diary-nonmarking-symbol)) 2412 (when (looking-at (regexp-quote diary-nonmarking-symbol))
2413 (replace-match "")) 2413 (replace-match ""))
2414 (unless (looking-at (regexp-quote diary-nonmarking-symbol)) 2414 (unless (looking-at (regexp-quote diary-nonmarking-symbol))
2415 (insert diary-nonmarking-symbol)))) 2415 (insert diary-nonmarking-symbol))))
2416 (todos-forward-item))))))) 2416 (todo-forward-item)))))))
2417 2417
2418(defun todos-set-item-priority (&optional item cat new arg) 2418(defun todo-set-item-priority (&optional item cat new arg)
2419 "Prompt for and set ITEM's priority in CATegory. 2419 "Prompt for and set ITEM's priority in CATegory.
2420 2420
2421Interactively, ITEM is the todo item at point, CAT is the current 2421Interactively, ITEM is the todo item at point, CAT is the current
@@ -2430,35 +2430,35 @@ whose value can be either of the symbols `raise' or `lower',
2430meaning to raise or lower the item's priority by one." 2430meaning to raise or lower the item's priority by one."
2431 (interactive) 2431 (interactive)
2432 (unless (and (called-interactively-p 'any) 2432 (unless (and (called-interactively-p 'any)
2433 (or (todos-done-item-p) (looking-at "^$"))) 2433 (or (todo-done-item-p) (looking-at "^$")))
2434 (let* ((item (or item (todos-item-string))) 2434 (let* ((item (or item (todo-item-string)))
2435 (marked (todos-marked-item-p)) 2435 (marked (todo-marked-item-p))
2436 (cat (or cat (cond ((eq major-mode 'todos-mode) 2436 (cat (or cat (cond ((eq major-mode 'todo-mode)
2437 (todos-current-category)) 2437 (todo-current-category))
2438 ((eq major-mode 'todos-filtered-items-mode) 2438 ((eq major-mode 'todo-filtered-items-mode)
2439 (let* ((regexp1 2439 (let* ((regexp1
2440 (concat todos-date-string-start 2440 (concat todo-date-string-start
2441 todos-date-pattern 2441 todo-date-pattern
2442 "\\( " diary-time-regexp "\\)?" 2442 "\\( " diary-time-regexp "\\)?"
2443 (regexp-quote todos-nondiary-end) 2443 (regexp-quote todo-nondiary-end)
2444 "?\\(?1: \\[\\(.+:\\)?.+\\]\\)"))) 2444 "?\\(?1: \\[\\(.+:\\)?.+\\]\\)")))
2445 (save-excursion 2445 (save-excursion
2446 (re-search-forward regexp1 nil t) 2446 (re-search-forward regexp1 nil t)
2447 (match-string-no-properties 1))))))) 2447 (match-string-no-properties 1)))))))
2448 curnum 2448 curnum
2449 (todo (cond ((or (eq arg 'raise) (eq arg 'lower) 2449 (todo (cond ((or (eq arg 'raise) (eq arg 'lower)
2450 (eq major-mode 'todos-filtered-items-mode)) 2450 (eq major-mode 'todo-filtered-items-mode))
2451 (save-excursion 2451 (save-excursion
2452 (let ((curstart (todos-item-start)) 2452 (let ((curstart (todo-item-start))
2453 (count 0)) 2453 (count 0))
2454 (goto-char (point-min)) 2454 (goto-char (point-min))
2455 (while (looking-at todos-item-start) 2455 (while (looking-at todo-item-start)
2456 (setq count (1+ count)) 2456 (setq count (1+ count))
2457 (when (= (point) curstart) (setq curnum count)) 2457 (when (= (point) curstart) (setq curnum count))
2458 (todos-forward-item)) 2458 (todo-forward-item))
2459 count))) 2459 count)))
2460 ((eq major-mode 'todos-mode) 2460 ((eq major-mode 'todo-mode)
2461 (todos-get-count 'todo cat)))) 2461 (todo-get-count 'todo cat))))
2462 (maxnum (if new (1+ todo) todo)) 2462 (maxnum (if new (1+ todo) todo))
2463 (prompt (format "Set item priority (1-%d): " maxnum)) 2463 (prompt (format "Set item priority (1-%d): " maxnum))
2464 (priority (cond ((and (not arg) (numberp current-prefix-arg)) 2464 (priority (cond ((and (not arg) (numberp current-prefix-arg))
@@ -2475,15 +2475,15 @@ meaning to raise or lower the item's priority by one."
2475 ;; When moving item to another category, show the category before 2475 ;; When moving item to another category, show the category before
2476 ;; prompting for its priority. 2476 ;; prompting for its priority.
2477 (unless (or arg (called-interactively-p 'any)) 2477 (unless (or arg (called-interactively-p 'any))
2478 (todos-category-number cat) 2478 (todo-category-number cat)
2479 ;; If done items in category are visible, keep them visible. 2479 ;; If done items in category are visible, keep them visible.
2480 (let ((done todos-show-with-done)) 2480 (let ((done todo-show-with-done))
2481 (when (> (buffer-size) (- (point-max) (point-min))) 2481 (when (> (buffer-size) (- (point-max) (point-min)))
2482 (save-excursion 2482 (save-excursion
2483 (goto-char (point-min)) 2483 (goto-char (point-min))
2484 (setq done (re-search-forward todos-done-string-start nil t)))) 2484 (setq done (re-search-forward todo-done-string-start nil t))))
2485 (let ((todos-show-with-done done)) 2485 (let ((todo-show-with-done done))
2486 (todos-category-select) 2486 (todo-category-select)
2487 ;; Keep top of category in view while setting priority. 2487 ;; Keep top of category in view while setting priority.
2488 (goto-char (point-min))))) 2488 (goto-char (point-min)))))
2489 ;; Prompt for priority only when the category has at least one 2489 ;; Prompt for priority only when the category has at least one
@@ -2498,93 +2498,93 @@ meaning to raise or lower the item's priority by one."
2498 ;; In Top Priorities buffer, an item's priority can be changed 2498 ;; In Top Priorities buffer, an item's priority can be changed
2499 ;; wrt items in another category, but not wrt items in the same 2499 ;; wrt items in another category, but not wrt items in the same
2500 ;; category. 2500 ;; category.
2501 (when (eq major-mode 'todos-filtered-items-mode) 2501 (when (eq major-mode 'todo-filtered-items-mode)
2502 (let* ((regexp2 (concat todos-date-string-start todos-date-pattern 2502 (let* ((regexp2 (concat todo-date-string-start todo-date-pattern
2503 "\\( " diary-time-regexp "\\)?" 2503 "\\( " diary-time-regexp "\\)?"
2504 (regexp-quote todos-nondiary-end) 2504 (regexp-quote todo-nondiary-end)
2505 "?\\(?1:" (regexp-quote cat) "\\)")) 2505 "?\\(?1:" (regexp-quote cat) "\\)"))
2506 (end (cond ((< curnum priority) 2506 (end (cond ((< curnum priority)
2507 (save-excursion (todos-item-end))) 2507 (save-excursion (todo-item-end)))
2508 ((> curnum priority) 2508 ((> curnum priority)
2509 (save-excursion (todos-item-start))))) 2509 (save-excursion (todo-item-start)))))
2510 (match (save-excursion 2510 (match (save-excursion
2511 (cond ((< curnum priority) 2511 (cond ((< curnum priority)
2512 (todos-forward-item (1+ (- priority curnum))) 2512 (todo-forward-item (1+ (- priority curnum)))
2513 (when (re-search-backward regexp2 end t) 2513 (when (re-search-backward regexp2 end t)
2514 (match-string-no-properties 1))) 2514 (match-string-no-properties 1)))
2515 ((> curnum priority) 2515 ((> curnum priority)
2516 (todos-backward-item (- curnum priority)) 2516 (todo-backward-item (- curnum priority))
2517 (when (re-search-forward regexp2 end t) 2517 (when (re-search-forward regexp2 end t)
2518 (match-string-no-properties 1))))))) 2518 (match-string-no-properties 1)))))))
2519 (when match 2519 (when match
2520 (user-error (concat "Cannot reprioritize items from the same " 2520 (user-error (concat "Cannot reprioritize items from the same "
2521 "category in this mode, only in Todos mode"))))) 2521 "category in this mode, only in Todo mode")))))
2522 ;; Interactively or with non-nil ARG, relocate the item within its 2522 ;; Interactively or with non-nil ARG, relocate the item within its
2523 ;; category. 2523 ;; category.
2524 (when (or arg (called-interactively-p 'any)) 2524 (when (or arg (called-interactively-p 'any))
2525 (todos-remove-item)) 2525 (todo-remove-item))
2526 (goto-char (point-min)) 2526 (goto-char (point-min))
2527 (when priority 2527 (when priority
2528 (unless (= priority 1) 2528 (unless (= priority 1)
2529 (todos-forward-item (1- priority)) 2529 (todo-forward-item (1- priority))
2530 ;; When called from todos-item-undone and the highest priority 2530 ;; When called from todo-item-undone and the highest priority
2531 ;; is chosen, this advances point to the first done item, so 2531 ;; is chosen, this advances point to the first done item, so
2532 ;; move it up to the empty line above the done items 2532 ;; move it up to the empty line above the done items
2533 ;; separator. 2533 ;; separator.
2534 (when (looking-back (concat "^" 2534 (when (looking-back (concat "^"
2535 (regexp-quote todos-category-done) 2535 (regexp-quote todo-category-done)
2536 "\n")) 2536 "\n"))
2537 (todos-backward-item)))) 2537 (todo-backward-item))))
2538 (todos-insert-with-overlays item) 2538 (todo-insert-with-overlays item)
2539 ;; If item was marked, restore the mark. 2539 ;; If item was marked, restore the mark.
2540 (and marked 2540 (and marked
2541 (let* ((ov (todos-get-overlay 'prefix)) 2541 (let* ((ov (todo-get-overlay 'prefix))
2542 (pref (overlay-get ov 'before-string))) 2542 (pref (overlay-get ov 'before-string)))
2543 (overlay-put ov 'before-string 2543 (overlay-put ov 'before-string
2544 (concat todos-item-mark pref)))))))) 2544 (concat todo-item-mark pref))))))))
2545 2545
2546(defun todos-raise-item-priority () 2546(defun todo-raise-item-priority ()
2547 "Raise priority of current item by moving it up by one item." 2547 "Raise priority of current item by moving it up by one item."
2548 (interactive) 2548 (interactive)
2549 (todos-set-item-priority nil nil nil 'raise)) 2549 (todo-set-item-priority nil nil nil 'raise))
2550 2550
2551(defun todos-lower-item-priority () 2551(defun todo-lower-item-priority ()
2552 "Lower priority of current item by moving it down by one item." 2552 "Lower priority of current item by moving it down by one item."
2553 (interactive) 2553 (interactive)
2554 (todos-set-item-priority nil nil nil 'lower)) 2554 (todo-set-item-priority nil nil nil 'lower))
2555 2555
2556(defun todos-move-item (&optional file) 2556(defun todo-move-item (&optional file)
2557 "Move at least one todo or done item to another category. 2557 "Move at least one todo or done item to another category.
2558If there are marked items, move all of these; otherwise, move 2558If there are marked items, move all of these; otherwise, move
2559the item at point. 2559the item at point.
2560 2560
2561With prefix argument FILE, prompt for a specific Todos file and 2561With prefix argument FILE, prompt for a specific Todo file and
2562choose (with TAB completion) a category in it to move the item or 2562choose (with TAB completion) a category in it to move the item or
2563items to; otherwise, choose and move to any category in either 2563items to; otherwise, choose and move to any category in either
2564the current Todos file or one of the files in 2564the current Todo file or one of the files in
2565`todos-category-completions-files'. If the chosen category is 2565`todo-category-completions-files'. If the chosen category is
2566not an existing categories, then it is created and the item(s) 2566not an existing categories, then it is created and the item(s)
2567become(s) the first entry/entries in that category. 2567become(s) the first entry/entries in that category.
2568 2568
2569With moved Todo items, prompt to set the priority in the category 2569With moved Todo items, prompt to set the priority in the category
2570moved to (with multiple todos items, the one that had the highest 2570moved to (with multiple todo items, the one that had the highest
2571priority in the category moved from gets the new priority and the 2571priority in the category moved from gets the new priority and the
2572rest of the moved todo items are inserted in sequence below it). 2572rest of the moved todo items are inserted in sequence below it).
2573Moved done items are appended to the top of the done items 2573Moved done items are appended to the top of the done items
2574section in the category moved to." 2574section in the category moved to."
2575 (interactive "P") 2575 (interactive "P")
2576 (let* ((cat1 (todos-current-category)) 2576 (let* ((cat1 (todo-current-category))
2577 (marked (assoc cat1 todos-categories-with-marks))) 2577 (marked (assoc cat1 todo-categories-with-marks)))
2578 ;; Noop if point is not on an item and there are no marked items. 2578 ;; Noop if point is not on an item and there are no marked items.
2579 (unless (and (looking-at "^$") 2579 (unless (and (looking-at "^$")
2580 (not marked)) 2580 (not marked))
2581 (let* ((buffer-read-only) 2581 (let* ((buffer-read-only)
2582 (file1 todos-current-todos-file) 2582 (file1 todo-current-todo-file)
2583 (num todos-category-number) 2583 (num todo-category-number)
2584 (item (todos-item-string)) 2584 (item (todo-item-string))
2585 (diary-item (todos-diary-item-p)) 2585 (diary-item (todo-diary-item-p))
2586 (done-item (and (todos-done-item-p) (concat item "\n"))) 2586 (done-item (and (todo-done-item-p) (concat item "\n")))
2587 (omark (save-excursion (todos-item-start) (point-marker))) 2587 (omark (save-excursion (todo-item-start) (point-marker)))
2588 (todo 0) 2588 (todo 0)
2589 (diary 0) 2589 (diary 0)
2590 (done 0) 2590 (done 0)
@@ -2592,16 +2592,16 @@ section in the category moved to."
2592 (unwind-protect 2592 (unwind-protect
2593 (progn 2593 (progn
2594 (unless marked 2594 (unless marked
2595 (setq ov (make-overlay (save-excursion (todos-item-start)) 2595 (setq ov (make-overlay (save-excursion (todo-item-start))
2596 (save-excursion (todos-item-end)))) 2596 (save-excursion (todo-item-end))))
2597 (overlay-put ov 'face 'todos-search)) 2597 (overlay-put ov 'face 'todo-search))
2598 (let* ((pl (if (and marked (> (cdr marked) 1)) "s" "")) 2598 (let* ((pl (if (and marked (> (cdr marked) 1)) "s" ""))
2599 (cat+file (todos-read-category (concat "Move item" pl 2599 (cat+file (todo-read-category (concat "Move item" pl
2600 " to category: ") 2600 " to category: ")
2601 nil file))) 2601 nil file)))
2602 (while (and (equal (car cat+file) cat1) 2602 (while (and (equal (car cat+file) cat1)
2603 (equal (cdr cat+file) file1)) 2603 (equal (cdr cat+file) file1))
2604 (setq cat+file (todos-read-category 2604 (setq cat+file (todo-read-category
2605 "Choose a different category: "))) 2605 "Choose a different category: ")))
2606 (setq cat2 (car cat+file) 2606 (setq cat2 (car cat+file)
2607 file2 (cdr cat+file)))) 2607 file2 (cdr cat+file))))
@@ -2611,43 +2611,43 @@ section in the category moved to."
2611 (progn 2611 (progn
2612 (goto-char (point-min)) 2612 (goto-char (point-min))
2613 (while (not (eobp)) 2613 (while (not (eobp))
2614 (when (todos-marked-item-p) 2614 (when (todo-marked-item-p)
2615 (if (todos-done-item-p) 2615 (if (todo-done-item-p)
2616 (setq done-items (concat done-items 2616 (setq done-items (concat done-items
2617 (todos-item-string) "\n") 2617 (todo-item-string) "\n")
2618 done (1+ done)) 2618 done (1+ done))
2619 (setq todo-items (concat todo-items 2619 (setq todo-items (concat todo-items
2620 (todos-item-string) "\n") 2620 (todo-item-string) "\n")
2621 todo (1+ todo)) 2621 todo (1+ todo))
2622 (when (todos-diary-item-p) 2622 (when (todo-diary-item-p)
2623 (setq diary (1+ diary))))) 2623 (setq diary (1+ diary)))))
2624 (todos-forward-item)) 2624 (todo-forward-item))
2625 ;; Chop off last newline of multiple todo item string, 2625 ;; Chop off last newline of multiple todo item string,
2626 ;; since it will be reinserted when setting priority 2626 ;; since it will be reinserted when setting priority
2627 ;; (but with done items priority is not set, so keep 2627 ;; (but with done items priority is not set, so keep
2628 ;; last newline). 2628 ;; last newline).
2629 (and todo-items 2629 (and todo-items
2630 (setq todo-items (substring todo-items 0 -1)))) 2630 (setq todo-items (substring todo-items 0 -1))))
2631 (if (todos-done-item-p) 2631 (if (todo-done-item-p)
2632 (setq done 1) 2632 (setq done 1)
2633 (setq todo 1) 2633 (setq todo 1)
2634 (when (todos-diary-item-p) (setq diary 1)))) 2634 (when (todo-diary-item-p) (setq diary 1))))
2635 (set-window-buffer (selected-window) 2635 (set-window-buffer (selected-window)
2636 (set-buffer (find-file-noselect file2 'nowarn))) 2636 (set-buffer (find-file-noselect file2 'nowarn)))
2637 (unwind-protect 2637 (unwind-protect
2638 (progn 2638 (progn
2639 (when (or todo-items (and item (not done-item))) 2639 (when (or todo-items (and item (not done-item)))
2640 (todos-set-item-priority (or todo-items item) cat2 t)) 2640 (todo-set-item-priority (or todo-items item) cat2 t))
2641 ;; Move done items en bloc to top of done items section. 2641 ;; Move done items en bloc to top of done items section.
2642 (when (or done-items done-item) 2642 (when (or done-items done-item)
2643 (todos-category-number cat2) 2643 (todo-category-number cat2)
2644 (widen) 2644 (widen)
2645 (goto-char (point-min)) 2645 (goto-char (point-min))
2646 (re-search-forward 2646 (re-search-forward
2647 (concat "^" (regexp-quote (concat todos-category-beg cat2)) 2647 (concat "^" (regexp-quote (concat todo-category-beg cat2))
2648 "$") nil t) 2648 "$") nil t)
2649 (re-search-forward 2649 (re-search-forward
2650 (concat "^" (regexp-quote todos-category-done)) nil t) 2650 (concat "^" (regexp-quote todo-category-done)) nil t)
2651 (forward-line) 2651 (forward-line)
2652 (insert (or done-items done-item))) 2652 (insert (or done-items done-item)))
2653 (setq moved t)) 2653 (setq moved t))
@@ -2657,10 +2657,10 @@ section in the category moved to."
2657 ;; the moved item. 2657 ;; the moved item.
2658 (moved 2658 (moved
2659 (setq nmark (point-marker)) 2659 (setq nmark (point-marker))
2660 (when todo (todos-update-count 'todo todo)) 2660 (when todo (todo-update-count 'todo todo))
2661 (when diary (todos-update-count 'diary diary)) 2661 (when diary (todo-update-count 'diary diary))
2662 (when done (todos-update-count 'done done)) 2662 (when done (todo-update-count 'done done))
2663 (todos-update-categories-sexp) 2663 (todo-update-categories-sexp)
2664 (with-current-buffer (find-buffer-visiting file1) 2664 (with-current-buffer (find-buffer-visiting file1)
2665 (save-excursion 2665 (save-excursion
2666 (save-restriction 2666 (save-restriction
@@ -2670,32 +2670,32 @@ section in the category moved to."
2670 (let (beg end) 2670 (let (beg end)
2671 (setq item nil) 2671 (setq item nil)
2672 (re-search-backward 2672 (re-search-backward
2673 (concat "^" (regexp-quote todos-category-beg)) nil t) 2673 (concat "^" (regexp-quote todo-category-beg)) nil t)
2674 (forward-line) 2674 (forward-line)
2675 (setq beg (point)) 2675 (setq beg (point))
2676 (setq end (if (re-search-forward 2676 (setq end (if (re-search-forward
2677 (concat "^" (regexp-quote 2677 (concat "^" (regexp-quote
2678 todos-category-beg)) nil t) 2678 todo-category-beg)) nil t)
2679 (match-beginning 0) 2679 (match-beginning 0)
2680 (point-max))) 2680 (point-max)))
2681 (goto-char beg) 2681 (goto-char beg)
2682 (while (< (point) end) 2682 (while (< (point) end)
2683 (if (todos-marked-item-p) 2683 (if (todo-marked-item-p)
2684 (todos-remove-item) 2684 (todo-remove-item)
2685 (todos-forward-item))) 2685 (todo-forward-item)))
2686 (setq todos-categories-with-marks 2686 (setq todo-categories-with-marks
2687 (assq-delete-all cat1 todos-categories-with-marks))) 2687 (assq-delete-all cat1 todo-categories-with-marks)))
2688 (if ov (delete-overlay ov)) 2688 (if ov (delete-overlay ov))
2689 (todos-remove-item)))) 2689 (todo-remove-item))))
2690 (when todo (todos-update-count 'todo (- todo) cat1)) 2690 (when todo (todo-update-count 'todo (- todo) cat1))
2691 (when diary (todos-update-count 'diary (- diary) cat1)) 2691 (when diary (todo-update-count 'diary (- diary) cat1))
2692 (when done (todos-update-count 'done (- done) cat1)) 2692 (when done (todo-update-count 'done (- done) cat1))
2693 (todos-update-categories-sexp)) 2693 (todo-update-categories-sexp))
2694 (set-window-buffer (selected-window) 2694 (set-window-buffer (selected-window)
2695 (set-buffer (find-file-noselect file2 'nowarn))) 2695 (set-buffer (find-file-noselect file2 'nowarn)))
2696 (setq todos-category-number (todos-category-number cat2)) 2696 (setq todo-category-number (todo-category-number cat2))
2697 (let ((todos-show-with-done (or done-items done-item))) 2697 (let ((todo-show-with-done (or done-items done-item)))
2698 (todos-category-select)) 2698 (todo-category-select))
2699 (goto-char nmark) 2699 (goto-char nmark)
2700 ;; If item is moved to end of (just first?) category, make 2700 ;; If item is moved to end of (just first?) category, make
2701 ;; sure the items above it are displayed in the window. 2701 ;; sure the items above it are displayed in the window.
@@ -2705,115 +2705,115 @@ section in the category moved to."
2705 (t 2705 (t
2706 (set-window-buffer (selected-window) 2706 (set-window-buffer (selected-window)
2707 (set-buffer (find-file-noselect file1 'nowarn))) 2707 (set-buffer (find-file-noselect file1 'nowarn)))
2708 (todos-category-number cat1) 2708 (todo-category-number cat1)
2709 (todos-category-select) 2709 (todo-category-select)
2710 (goto-char omark)))))))) 2710 (goto-char omark))))))))
2711 2711
2712(defun todos-item-done (&optional arg) 2712(defun todo-item-done (&optional arg)
2713 "Tag a todo item in this category as done and relocate it. 2713 "Tag a todo item in this category as done and relocate it.
2714 2714
2715With prefix argument ARG prompt for a comment and append it to 2715With prefix argument ARG prompt for a comment and append it to
2716the done item; this is only possible if there are no marked 2716the done item; this is only possible if there are no marked
2717items. If there are marked items, tag all of these with 2717items. If there are marked items, tag all of these with
2718`todos-done-string' plus the current date and, if 2718`todo-done-string' plus the current date and, if
2719`todos-always-add-time-string' is non-nil, the current time; 2719`todo-always-add-time-string' is non-nil, the current time;
2720otherwise, just tag the item at point. Items tagged as done are 2720otherwise, just tag the item at point. Items tagged as done are
2721relocated to the category's (by default hidden) done section. If 2721relocated to the category's (by default hidden) done section. If
2722done items are visible on invoking this command, they remain 2722done items are visible on invoking this command, they remain
2723visible." 2723visible."
2724 (interactive "P") 2724 (interactive "P")
2725 (let* ((cat (todos-current-category)) 2725 (let* ((cat (todo-current-category))
2726 (marked (assoc cat todos-categories-with-marks))) 2726 (marked (assoc cat todo-categories-with-marks)))
2727 (when marked 2727 (when marked
2728 (save-excursion 2728 (save-excursion
2729 (save-restriction 2729 (save-restriction
2730 (goto-char (point-max)) 2730 (goto-char (point-max))
2731 (todos-backward-item) 2731 (todo-backward-item)
2732 (unless (todos-done-item-p) 2732 (unless (todo-done-item-p)
2733 (widen) 2733 (widen)
2734 (unless (re-search-forward 2734 (unless (re-search-forward
2735 (concat "^" (regexp-quote todos-category-beg)) nil t) 2735 (concat "^" (regexp-quote todo-category-beg)) nil t)
2736 (goto-char (point-max))) 2736 (goto-char (point-max)))
2737 (forward-line -1)) 2737 (forward-line -1))
2738 (while (todos-done-item-p) 2738 (while (todo-done-item-p)
2739 (when (todos-marked-item-p) 2739 (when (todo-marked-item-p)
2740 (user-error "This command does not apply to done items")) 2740 (user-error "This command does not apply to done items"))
2741 (todos-backward-item))))) 2741 (todo-backward-item)))))
2742 (unless (and (not marked) 2742 (unless (and (not marked)
2743 (or (todos-done-item-p) 2743 (or (todo-done-item-p)
2744 ;; Point is between todo and done items. 2744 ;; Point is between todo and done items.
2745 (looking-at "^$"))) 2745 (looking-at "^$")))
2746 (let* ((date-string (calendar-date-string (calendar-current-date) t t)) 2746 (let* ((date-string (calendar-date-string (calendar-current-date) t t))
2747 (time-string (if todos-always-add-time-string 2747 (time-string (if todo-always-add-time-string
2748 (concat " " (substring (current-time-string) 2748 (concat " " (substring (current-time-string)
2749 11 16)) 2749 11 16))
2750 "")) 2750 ""))
2751 (done-prefix (concat "[" todos-done-string date-string time-string 2751 (done-prefix (concat "[" todo-done-string date-string time-string
2752 "] ")) 2752 "] "))
2753 (comment (and arg (read-string "Enter a comment: "))) 2753 (comment (and arg (read-string "Enter a comment: ")))
2754 (item-count 0) 2754 (item-count 0)
2755 (diary-count 0) 2755 (diary-count 0)
2756 (show-done (save-excursion 2756 (show-done (save-excursion
2757 (goto-char (point-min)) 2757 (goto-char (point-min))
2758 (re-search-forward todos-done-string-start nil t))) 2758 (re-search-forward todo-done-string-start nil t)))
2759 (buffer-read-only nil) 2759 (buffer-read-only nil)
2760 item done-item opoint) 2760 item done-item opoint)
2761 ;; Don't add empty comment to done item. 2761 ;; Don't add empty comment to done item.
2762 (setq comment (unless (zerop (length comment)) 2762 (setq comment (unless (zerop (length comment))
2763 (concat " [" todos-comment-string ": " comment "]"))) 2763 (concat " [" todo-comment-string ": " comment "]")))
2764 (and marked (goto-char (point-min))) 2764 (and marked (goto-char (point-min)))
2765 (catch 'done 2765 (catch 'done
2766 ;; Stop looping when we hit the empty line below the last 2766 ;; Stop looping when we hit the empty line below the last
2767 ;; todo item (this is eobp if only done items are hidden). 2767 ;; todo item (this is eobp if only done items are hidden).
2768 (while (not (looking-at "^$")) 2768 (while (not (looking-at "^$"))
2769 (if (or (not marked) (and marked (todos-marked-item-p))) 2769 (if (or (not marked) (and marked (todo-marked-item-p)))
2770 (progn 2770 (progn
2771 (setq item (todos-item-string)) 2771 (setq item (todo-item-string))
2772 (setq done-item (concat done-item done-prefix item 2772 (setq done-item (concat done-item done-prefix item
2773 comment (and marked "\n"))) 2773 comment (and marked "\n")))
2774 (setq item-count (1+ item-count)) 2774 (setq item-count (1+ item-count))
2775 (when (todos-diary-item-p) 2775 (when (todo-diary-item-p)
2776 (setq diary-count (1+ diary-count))) 2776 (setq diary-count (1+ diary-count)))
2777 (todos-remove-item) 2777 (todo-remove-item)
2778 (unless marked (throw 'done nil))) 2778 (unless marked (throw 'done nil)))
2779 (todos-forward-item)))) 2779 (todo-forward-item))))
2780 (when marked 2780 (when marked
2781 ;; Chop off last newline of done item string. 2781 ;; Chop off last newline of done item string.
2782 (setq done-item (substring done-item 0 -1)) 2782 (setq done-item (substring done-item 0 -1))
2783 (setq todos-categories-with-marks 2783 (setq todo-categories-with-marks
2784 (assq-delete-all cat todos-categories-with-marks))) 2784 (assq-delete-all cat todo-categories-with-marks)))
2785 (save-excursion 2785 (save-excursion
2786 (widen) 2786 (widen)
2787 (re-search-forward 2787 (re-search-forward
2788 (concat "^" (regexp-quote todos-category-done)) nil t) 2788 (concat "^" (regexp-quote todo-category-done)) nil t)
2789 (forward-char) 2789 (forward-char)
2790 (when show-done (setq opoint (point))) 2790 (when show-done (setq opoint (point)))
2791 (insert done-item "\n")) 2791 (insert done-item "\n"))
2792 (todos-update-count 'todo (- item-count)) 2792 (todo-update-count 'todo (- item-count))
2793 (todos-update-count 'done item-count) 2793 (todo-update-count 'done item-count)
2794 (todos-update-count 'diary (- diary-count)) 2794 (todo-update-count 'diary (- diary-count))
2795 (todos-update-categories-sexp) 2795 (todo-update-categories-sexp)
2796 (let ((todos-show-with-done show-done)) 2796 (let ((todo-show-with-done show-done))
2797 (todos-category-select) 2797 (todo-category-select)
2798 ;; When done items are shown, put cursor on first just done item. 2798 ;; When done items are shown, put cursor on first just done item.
2799 (when opoint (goto-char opoint))))))) 2799 (when opoint (goto-char opoint)))))))
2800 2800
2801(defun todos-edit-done-item-comment (&optional arg) 2801(defun todo-edit-done-item-comment (&optional arg)
2802 "Add a comment to this done item or edit an existing comment. 2802 "Add a comment to this done item or edit an existing comment.
2803With prefix ARG delete an existing comment." 2803With prefix ARG delete an existing comment."
2804 (interactive "P") 2804 (interactive "P")
2805 (when (todos-done-item-p) 2805 (when (todo-done-item-p)
2806 (let ((item (todos-item-string)) 2806 (let ((item (todo-item-string))
2807 (opoint (point)) 2807 (opoint (point))
2808 (end (save-excursion (todos-item-end))) 2808 (end (save-excursion (todo-item-end)))
2809 comment buffer-read-only) 2809 comment buffer-read-only)
2810 (save-excursion 2810 (save-excursion
2811 (todos-item-start) 2811 (todo-item-start)
2812 (if (re-search-forward (concat " \\[" 2812 (if (re-search-forward (concat " \\["
2813 (regexp-quote todos-comment-string) 2813 (regexp-quote todo-comment-string)
2814 ": \\([^]]+\\)\\]") end t) 2814 ": \\([^]]+\\)\\]") end t)
2815 (if arg 2815 (if arg
2816 (when (todos-y-or-n-p "Delete comment? ") 2816 (when (todo-y-or-n-p "Delete comment? ")
2817 (delete-region (match-beginning 0) (match-end 0))) 2817 (delete-region (match-beginning 0) (match-end 0)))
2818 (setq comment (read-string "Edit comment: " 2818 (setq comment (read-string "Edit comment: "
2819 (cons (match-string 1) 1))) 2819 (cons (match-string 1) 1)))
@@ -2821,10 +2821,10 @@ With prefix ARG delete an existing comment."
2821 (setq comment (read-string "Enter a comment: ")) 2821 (setq comment (read-string "Enter a comment: "))
2822 ;; If user moved point during editing, make sure it moves back. 2822 ;; If user moved point during editing, make sure it moves back.
2823 (goto-char opoint) 2823 (goto-char opoint)
2824 (todos-item-end) 2824 (todo-item-end)
2825 (insert " [" todos-comment-string ": " comment "]")))))) 2825 (insert " [" todo-comment-string ": " comment "]"))))))
2826 2826
2827(defun todos-item-undone () 2827(defun todo-item-undone ()
2828 "Restore at least one done item to this category's todo section. 2828 "Restore at least one done item to this category's todo section.
2829Prompt for the new priority. If there are marked items, undo all 2829Prompt for the new priority. If there are marked items, undo all
2830of these, giving the first undone item the new priority and the 2830of these, giving the first undone item the new priority and the
@@ -2836,10 +2836,10 @@ from the restored item. With multiple marked done items with
2836comments, only ask once, and if affirmed, omit subsequent 2836comments, only ask once, and if affirmed, omit subsequent
2837comments without asking." 2837comments without asking."
2838 (interactive) 2838 (interactive)
2839 (let* ((cat (todos-current-category)) 2839 (let* ((cat (todo-current-category))
2840 (marked (assoc cat todos-categories-with-marks)) 2840 (marked (assoc cat todo-categories-with-marks))
2841 (pl (if (and marked (> (cdr marked) 1)) "s" ""))) 2841 (pl (if (and marked (> (cdr marked) 1)) "s" "")))
2842 (when (or marked (todos-done-item-p)) 2842 (when (or marked (todo-done-item-p))
2843 (let ((buffer-read-only) 2843 (let ((buffer-read-only)
2844 (opoint (point)) 2844 (opoint (point))
2845 (omark (point-marker)) 2845 (omark (point-marker))
@@ -2850,36 +2850,36 @@ comments without asking."
2850 (and marked (goto-char (point-min))) 2850 (and marked (goto-char (point-min)))
2851 (catch 'done 2851 (catch 'done
2852 (while (not (eobp)) 2852 (while (not (eobp))
2853 (when (or (not marked) (and marked (todos-marked-item-p))) 2853 (when (or (not marked) (and marked (todo-marked-item-p)))
2854 (if (not (todos-done-item-p)) 2854 (if (not (todo-done-item-p))
2855 (user-error "Only done items can be undone") 2855 (user-error "Only done items can be undone")
2856 (todos-item-start) 2856 (todo-item-start)
2857 (unless marked 2857 (unless marked
2858 (setq ov (make-overlay (save-excursion (todos-item-start)) 2858 (setq ov (make-overlay (save-excursion (todo-item-start))
2859 (save-excursion (todos-item-end)))) 2859 (save-excursion (todo-item-end))))
2860 (overlay-put ov 'face 'todos-search)) 2860 (overlay-put ov 'face 'todo-search))
2861 ;; Find the end of the date string added upon tagging item as 2861 ;; Find the end of the date string added upon tagging item as
2862 ;; done. 2862 ;; done.
2863 (setq start (search-forward "] ")) 2863 (setq start (search-forward "] "))
2864 (setq item-count (1+ item-count)) 2864 (setq item-count (1+ item-count))
2865 (unless (looking-at (regexp-quote todos-nondiary-start)) 2865 (unless (looking-at (regexp-quote todo-nondiary-start))
2866 (setq diary-count (1+ diary-count))) 2866 (setq diary-count (1+ diary-count)))
2867 (setq end (save-excursion (todos-item-end))) 2867 (setq end (save-excursion (todo-item-end)))
2868 ;; Ask (once) whether to omit done item's comment. If 2868 ;; Ask (once) whether to omit done item's comment. If
2869 ;; affirmed, omit subsequent comments without asking. 2869 ;; affirmed, omit subsequent comments without asking.
2870 (when (re-search-forward 2870 (when (re-search-forward
2871 (concat " \\[" (regexp-quote todos-comment-string) 2871 (concat " \\[" (regexp-quote todo-comment-string)
2872 ": [^]]+\\]") end t) 2872 ": [^]]+\\]") end t)
2873 (unwind-protect 2873 (unwind-protect
2874 (if (eq first 'first) 2874 (if (eq first 'first)
2875 (setq first 2875 (setq first
2876 (if (eq todos-undo-item-omit-comment 'ask) 2876 (if (eq todo-undo-item-omit-comment 'ask)
2877 (when (todos-y-or-n-p 2877 (when (todo-y-or-n-p
2878 (concat "Omit comment" pl 2878 (concat "Omit comment" pl
2879 " from restored item" 2879 " from restored item"
2880 pl "? ")) 2880 pl "? "))
2881 'omit) 2881 'omit)
2882 (when todos-undo-item-omit-comment 'omit))) 2882 (when todo-undo-item-omit-comment 'omit)))
2883 t) 2883 t)
2884 (when (and (eq first 'first) ov) (delete-overlay ov))) 2884 (when (and (eq first 'first) ov) (delete-overlay ov)))
2885 (when (eq first 'omit) 2885 (when (eq first 'omit)
@@ -2888,13 +2888,13 @@ comments without asking."
2888 (buffer-substring-no-properties start end) 2888 (buffer-substring-no-properties start end)
2889 (when marked "\n"))) 2889 (when marked "\n")))
2890 (unless marked (throw 'done nil)))) 2890 (unless marked (throw 'done nil))))
2891 (todos-forward-item))) 2891 (todo-forward-item)))
2892 (unwind-protect 2892 (unwind-protect
2893 (progn 2893 (progn
2894 ;; Chop off last newline of multiple items string, since 2894 ;; Chop off last newline of multiple items string, since
2895 ;; it will be reinserted on setting priority. 2895 ;; it will be reinserted on setting priority.
2896 (and marked (setq item (substring item 0 -1))) 2896 (and marked (setq item (substring item 0 -1)))
2897 (todos-set-item-priority item cat t) 2897 (todo-set-item-priority item cat t)
2898 (setq npoint (point)) 2898 (setq npoint (point))
2899 (setq undone t)) 2899 (setq undone t))
2900 (when ov (delete-overlay ov)) 2900 (when ov (delete-overlay ov))
@@ -2904,21 +2904,21 @@ comments without asking."
2904 (progn 2904 (progn
2905 (setq item nil) 2905 (setq item nil)
2906 (re-search-forward 2906 (re-search-forward
2907 (concat "^" (regexp-quote todos-category-done)) nil t) 2907 (concat "^" (regexp-quote todo-category-done)) nil t)
2908 (while (not (eobp)) 2908 (while (not (eobp))
2909 (if (todos-marked-item-p) 2909 (if (todo-marked-item-p)
2910 (todos-remove-item) 2910 (todo-remove-item)
2911 (todos-forward-item))) 2911 (todo-forward-item)))
2912 (setq todos-categories-with-marks 2912 (setq todo-categories-with-marks
2913 (assq-delete-all cat todos-categories-with-marks))) 2913 (assq-delete-all cat todo-categories-with-marks)))
2914 (goto-char omark) 2914 (goto-char omark)
2915 (todos-remove-item)) 2915 (todo-remove-item))
2916 (todos-update-count 'todo item-count) 2916 (todo-update-count 'todo item-count)
2917 (todos-update-count 'done (- item-count)) 2917 (todo-update-count 'done (- item-count))
2918 (when diary-count (todos-update-count 'diary diary-count)) 2918 (when diary-count (todo-update-count 'diary diary-count))
2919 (todos-update-categories-sexp) 2919 (todo-update-categories-sexp)
2920 (let ((todos-show-with-done (> (todos-get-count 'done) 0))) 2920 (let ((todo-show-with-done (> (todo-get-count 'done) 0)))
2921 (todos-category-select)) 2921 (todo-category-select))
2922 ;; Put cursor on undone item. 2922 ;; Put cursor on undone item.
2923 (goto-char npoint))) 2923 (goto-char npoint)))
2924 (set-marker omark nil))))) 2924 (set-marker omark nil)))))
@@ -2927,32 +2927,32 @@ comments without asking."
2927;;; Done item archives 2927;;; Done item archives
2928;; ----------------------------------------------------------------------------- 2928;; -----------------------------------------------------------------------------
2929 2929
2930(defun todos-find-archive (&optional ask) 2930(defun todo-find-archive (&optional ask)
2931 "Visit the archive of the current Todos category, if it exists. 2931 "Visit the archive of the current Todo category, if it exists.
2932If the category has no archived items, prompt to visit the 2932If the category has no archived items, prompt to visit the
2933archive anyway. If there is no archive for this file or with 2933archive anyway. If there is no archive for this file or with
2934non-nil argument ASK, prompt to visit another archive. 2934non-nil argument ASK, prompt to visit another archive.
2935 2935
2936The buffer showing the archive is in Todos Archive mode. The 2936The buffer showing the archive is in Todo Archive mode. The
2937first visit in a session displays the first category in the 2937first visit in a session displays the first category in the
2938archive, subsequent visits return to the last category 2938archive, subsequent visits return to the last category
2939displayed." 2939displayed."
2940 (interactive) 2940 (interactive)
2941 (let* ((cat (todos-current-category)) 2941 (let* ((cat (todo-current-category))
2942 (count (todos-get-count 'archived cat)) 2942 (count (todo-get-count 'archived cat))
2943 (archive (concat (file-name-sans-extension todos-current-todos-file) 2943 (archive (concat (file-name-sans-extension todo-current-todo-file)
2944 ".toda")) 2944 ".toda"))
2945 place) 2945 place)
2946 (setq place (cond (ask 'other-archive) 2946 (setq place (cond (ask 'other-archive)
2947 ((file-exists-p archive) 'this-archive) 2947 ((file-exists-p archive) 'this-archive)
2948 (t (when (todos-y-or-n-p 2948 (t (when (todo-y-or-n-p
2949 (concat "This file has no archive; " 2949 (concat "This file has no archive; "
2950 "visit another archive? ")) 2950 "visit another archive? "))
2951 'other-archive)))) 2951 'other-archive))))
2952 (when (eq place 'other-archive) 2952 (when (eq place 'other-archive)
2953 (setq archive (todos-read-file-name "Choose a Todos archive: " t t))) 2953 (setq archive (todo-read-file-name "Choose a Todo archive: " t t)))
2954 (when (and (eq place 'this-archive) (zerop count)) 2954 (when (and (eq place 'this-archive) (zerop count))
2955 (setq place (when (todos-y-or-n-p 2955 (setq place (when (todo-y-or-n-p
2956 (concat "This category has no archived items;" 2956 (concat "This category has no archived items;"
2957 " visit archive anyway? ")) 2957 " visit archive anyway? "))
2958 'other-cat))) 2958 'other-cat)))
@@ -2960,16 +2960,16 @@ displayed."
2960 (set-window-buffer (selected-window) 2960 (set-window-buffer (selected-window)
2961 (set-buffer (find-file-noselect archive))) 2961 (set-buffer (find-file-noselect archive)))
2962 (if (member place '(other-archive other-cat)) 2962 (if (member place '(other-archive other-cat))
2963 (setq todos-category-number 1) 2963 (setq todo-category-number 1)
2964 (todos-category-number cat)) 2964 (todo-category-number cat))
2965 (todos-category-select)))) 2965 (todo-category-select))))
2966 2966
2967(defun todos-choose-archive () 2967(defun todo-choose-archive ()
2968 "Choose an archive and visit it." 2968 "Choose an archive and visit it."
2969 (interactive) 2969 (interactive)
2970 (todos-find-archive t)) 2970 (todo-find-archive t))
2971 2971
2972(defun todos-archive-done-item (&optional all) 2972(defun todo-archive-done-item (&optional all)
2973 "Archive at least one done item in this category. 2973 "Archive at least one done item in this category.
2974 2974
2975With prefix argument ALL, prompt whether to archive all done 2975With prefix argument ALL, prompt whether to archive all done
@@ -2981,43 +2981,43 @@ point.
2981If the archive of this file does not exist, it is created. If 2981If the archive of this file does not exist, it is created. If
2982this category does not exist in the archive, it is created." 2982this category does not exist in the archive, it is created."
2983 (interactive "P") 2983 (interactive "P")
2984 (when (eq major-mode 'todos-mode) 2984 (when (eq major-mode 'todo-mode)
2985 (if (and all (zerop (todos-get-count 'done))) 2985 (if (and all (zerop (todo-get-count 'done)))
2986 (message "No done items in this category") 2986 (message "No done items in this category")
2987 (catch 'end 2987 (catch 'end
2988 (let* ((cat (todos-current-category)) 2988 (let* ((cat (todo-current-category))
2989 (tbuf (current-buffer)) 2989 (tbuf (current-buffer))
2990 (marked (assoc cat todos-categories-with-marks)) 2990 (marked (assoc cat todo-categories-with-marks))
2991 (afile (concat (file-name-sans-extension 2991 (afile (concat (file-name-sans-extension
2992 todos-current-todos-file) ".toda")) 2992 todo-current-todo-file) ".toda"))
2993 (archive (if (file-exists-p afile) 2993 (archive (if (file-exists-p afile)
2994 (find-file-noselect afile t) 2994 (find-file-noselect afile t)
2995 (get-buffer-create afile))) 2995 (get-buffer-create afile)))
2996 (item (and (todos-done-item-p) 2996 (item (and (todo-done-item-p)
2997 (concat (todos-item-string) "\n"))) 2997 (concat (todo-item-string) "\n")))
2998 (count 0) 2998 (count 0)
2999 (opoint (unless (todos-done-item-p) (point))) 2999 (opoint (unless (todo-done-item-p) (point)))
3000 marked-items beg end all-done 3000 marked-items beg end all-done
3001 buffer-read-only) 3001 buffer-read-only)
3002 (cond 3002 (cond
3003 (all 3003 (all
3004 (if (todos-y-or-n-p "Archive all done items in this category? ") 3004 (if (todo-y-or-n-p "Archive all done items in this category? ")
3005 (save-excursion 3005 (save-excursion
3006 (save-restriction 3006 (save-restriction
3007 (goto-char (point-min)) 3007 (goto-char (point-min))
3008 (widen) 3008 (widen)
3009 (setq beg (progn 3009 (setq beg (progn
3010 (re-search-forward todos-done-string-start 3010 (re-search-forward todo-done-string-start
3011 nil t) 3011 nil t)
3012 (match-beginning 0)) 3012 (match-beginning 0))
3013 end (if (re-search-forward 3013 end (if (re-search-forward
3014 (concat "^" 3014 (concat "^"
3015 (regexp-quote todos-category-beg)) 3015 (regexp-quote todo-category-beg))
3016 nil t) 3016 nil t)
3017 (match-beginning 0) 3017 (match-beginning 0)
3018 (point-max)) 3018 (point-max))
3019 all-done (buffer-substring-no-properties beg end) 3019 all-done (buffer-substring-no-properties beg end)
3020 count (todos-get-count 'done)) 3020 count (todo-get-count 'done))
3021 ;; Restore starting point, unless it was on a done 3021 ;; Restore starting point, unless it was on a done
3022 ;; item, since they will all be deleted. 3022 ;; item, since they will all be deleted.
3023 (when opoint (goto-char opoint)))) 3023 (when opoint (goto-char opoint))))
@@ -3026,13 +3026,13 @@ this category does not exist in the archive, it is created."
3026 (save-excursion 3026 (save-excursion
3027 (goto-char (point-min)) 3027 (goto-char (point-min))
3028 (while (not (eobp)) 3028 (while (not (eobp))
3029 (when (todos-marked-item-p) 3029 (when (todo-marked-item-p)
3030 (if (not (todos-done-item-p)) 3030 (if (not (todo-done-item-p))
3031 (throw 'end (message "Only done items can be archived")) 3031 (throw 'end (message "Only done items can be archived"))
3032 (setq marked-items 3032 (setq marked-items
3033 (concat marked-items (todos-item-string) "\n")) 3033 (concat marked-items (todo-item-string) "\n"))
3034 (setq count (1+ count)))) 3034 (setq count (1+ count))))
3035 (todos-forward-item))))) 3035 (todo-forward-item)))))
3036 (if (not (or marked all item)) 3036 (if (not (or marked all item))
3037 (throw 'end (message "Only done items can be archived")) 3037 (throw 'end (message "Only done items can be archived"))
3038 (with-current-buffer archive 3038 (with-current-buffer archive
@@ -3042,20 +3042,20 @@ this category does not exist in the archive, it is created."
3042 (goto-char (point-min)) 3042 (goto-char (point-min))
3043 (if (and (re-search-forward 3043 (if (and (re-search-forward
3044 (concat "^" (regexp-quote 3044 (concat "^" (regexp-quote
3045 (concat todos-category-beg cat)) "$") 3045 (concat todo-category-beg cat)) "$")
3046 nil t) 3046 nil t)
3047 (re-search-forward (regexp-quote todos-category-done) 3047 (re-search-forward (regexp-quote todo-category-done)
3048 nil t)) 3048 nil t))
3049 ;; Start of done items section in existing category. 3049 ;; Start of done items section in existing category.
3050 (forward-char) 3050 (forward-char)
3051 (todos-add-category nil cat) 3051 (todo-add-category nil cat)
3052 ;; Start of done items section in new category. 3052 ;; Start of done items section in new category.
3053 (goto-char (point-max))) 3053 (goto-char (point-max)))
3054 (insert (cond (marked marked-items) 3054 (insert (cond (marked marked-items)
3055 (all all-done) 3055 (all all-done)
3056 (item))) 3056 (item)))
3057 (todos-update-count 'done (if (or marked all) count 1) cat) 3057 (todo-update-count 'done (if (or marked all) count 1) cat)
3058 (todos-update-categories-sexp) 3058 (todo-update-categories-sexp)
3059 ;; If archive is new, save to file now (using write-region in 3059 ;; If archive is new, save to file now (using write-region in
3060 ;; order not to get prompted for file to save to), to let 3060 ;; order not to get prompted for file to save to), to let
3061 ;; auto-mode-alist take effect below. 3061 ;; auto-mode-alist take effect below.
@@ -3071,8 +3071,8 @@ this category does not exist in the archive, it is created."
3071 (widen) 3071 (widen)
3072 (remove-overlays beg end) 3072 (remove-overlays beg end)
3073 (delete-region beg end) 3073 (delete-region beg end)
3074 (todos-update-count 'done (- count)) 3074 (todo-update-count 'done (- count))
3075 (todos-update-count 'archived count)))) 3075 (todo-update-count 'archived count))))
3076 ((or marked 3076 ((or marked
3077 ;; If we're archiving all done items, can't 3077 ;; If we're archiving all done items, can't
3078 ;; first archive item point was on, since 3078 ;; first archive item point was on, since
@@ -3081,51 +3081,51 @@ this category does not exist in the archive, it is created."
3081 (and marked (goto-char (point-min))) 3081 (and marked (goto-char (point-min)))
3082 (catch 'done 3082 (catch 'done
3083 (while (not (eobp)) 3083 (while (not (eobp))
3084 (if (or (and marked (todos-marked-item-p)) item) 3084 (if (or (and marked (todo-marked-item-p)) item)
3085 (progn 3085 (progn
3086 (todos-remove-item) 3086 (todo-remove-item)
3087 (todos-update-count 'done -1) 3087 (todo-update-count 'done -1)
3088 (todos-update-count 'archived 1) 3088 (todo-update-count 'archived 1)
3089 ;; Don't leave point below last item. 3089 ;; Don't leave point below last item.
3090 (and item (bolp) (eolp) (< (point-min) (point-max)) 3090 (and item (bolp) (eolp) (< (point-min) (point-max))
3091 (todos-backward-item)) 3091 (todo-backward-item))
3092 (when item 3092 (when item
3093 (throw 'done (setq item nil)))) 3093 (throw 'done (setq item nil))))
3094 (todos-forward-item)))))) 3094 (todo-forward-item))))))
3095 (when marked 3095 (when marked
3096 (setq todos-categories-with-marks 3096 (setq todo-categories-with-marks
3097 (assq-delete-all cat todos-categories-with-marks))) 3097 (assq-delete-all cat todo-categories-with-marks)))
3098 (todos-update-categories-sexp) 3098 (todo-update-categories-sexp)
3099 (todos-prefix-overlays))) 3099 (todo-prefix-overlays)))
3100 (find-file afile) 3100 (find-file afile)
3101 (todos-category-number cat) 3101 (todo-category-number cat)
3102 (todos-category-select) 3102 (todo-category-select)
3103 (split-window-below) 3103 (split-window-below)
3104 (set-window-buffer (selected-window) tbuf) 3104 (set-window-buffer (selected-window) tbuf)
3105 ;; Make todo file current to select category. 3105 ;; Make todo file current to select category.
3106 (find-file (buffer-file-name tbuf)) 3106 (find-file (buffer-file-name tbuf))
3107 ;; Make sure done item separator is hidden (if done items 3107 ;; Make sure done item separator is hidden (if done items
3108 ;; were initially visible). 3108 ;; were initially visible).
3109 (let (todos-show-with-done) (todos-category-select))))))) 3109 (let (todo-show-with-done) (todo-category-select)))))))
3110 3110
3111(defun todos-unarchive-items () 3111(defun todo-unarchive-items ()
3112 "Unarchive at least one item in this archive category. 3112 "Unarchive at least one item in this archive category.
3113If there are marked items, unarchive all of these; otherwise, 3113If there are marked items, unarchive all of these; otherwise,
3114unarchive the item at point. 3114unarchive the item at point.
3115 3115
3116Unarchived items are restored as done items to the corresponding 3116Unarchived items are restored as done items to the corresponding
3117category in the Todos file, inserted at the top of done items 3117category in the Todo file, inserted at the top of done items
3118section. If all items in the archive category have been 3118section. If all items in the archive category have been
3119restored, the category is deleted from the archive. If this was 3119restored, the category is deleted from the archive. If this was
3120the only category in the archive, the archive file is deleted." 3120the only category in the archive, the archive file is deleted."
3121 (interactive) 3121 (interactive)
3122 (when (eq major-mode 'todos-archive-mode) 3122 (when (eq major-mode 'todo-archive-mode)
3123 (let* ((cat (todos-current-category)) 3123 (let* ((cat (todo-current-category))
3124 (tbuf (find-file-noselect 3124 (tbuf (find-file-noselect
3125 (concat (file-name-sans-extension todos-current-todos-file) 3125 (concat (file-name-sans-extension todo-current-todo-file)
3126 ".todo") t)) 3126 ".todo") t))
3127 (marked (assoc cat todos-categories-with-marks)) 3127 (marked (assoc cat todo-categories-with-marks))
3128 (item (concat (todos-item-string) "\n")) 3128 (item (concat (todo-item-string) "\n"))
3129 (marked-count 0) 3129 (marked-count 0)
3130 marked-items 3130 marked-items
3131 buffer-read-only) 3131 buffer-read-only)
@@ -3133,10 +3133,10 @@ the only category in the archive, the archive file is deleted."
3133 (save-excursion 3133 (save-excursion
3134 (goto-char (point-min)) 3134 (goto-char (point-min))
3135 (while (not (eobp)) 3135 (while (not (eobp))
3136 (when (todos-marked-item-p) 3136 (when (todo-marked-item-p)
3137 (setq marked-items (concat marked-items (todos-item-string) "\n")) 3137 (setq marked-items (concat marked-items (todo-item-string) "\n"))
3138 (setq marked-count (1+ marked-count))) 3138 (setq marked-count (1+ marked-count)))
3139 (todos-forward-item)))) 3139 (todo-forward-item))))
3140 ;; Restore items to top of category's done section and update counts. 3140 ;; Restore items to top of category's done section and update counts.
3141 (with-current-buffer tbuf 3141 (with-current-buffer tbuf
3142 (let (buffer-read-only newcat) 3142 (let (buffer-read-only newcat)
@@ -3145,42 +3145,42 @@ the only category in the archive, the archive file is deleted."
3145 ;; Find the corresponding todo category, or if there isn't 3145 ;; Find the corresponding todo category, or if there isn't
3146 ;; one, add it. 3146 ;; one, add it.
3147 (unless (re-search-forward 3147 (unless (re-search-forward
3148 (concat "^" (regexp-quote (concat todos-category-beg cat)) 3148 (concat "^" (regexp-quote (concat todo-category-beg cat))
3149 "$") nil t) 3149 "$") nil t)
3150 (todos-add-category nil cat) 3150 (todo-add-category nil cat)
3151 (setq newcat t)) 3151 (setq newcat t))
3152 ;; Go to top of category's done section. 3152 ;; Go to top of category's done section.
3153 (re-search-forward 3153 (re-search-forward
3154 (concat "^" (regexp-quote todos-category-done)) nil t) 3154 (concat "^" (regexp-quote todo-category-done)) nil t)
3155 (forward-line) 3155 (forward-line)
3156 (cond (marked 3156 (cond (marked
3157 (insert marked-items) 3157 (insert marked-items)
3158 (todos-update-count 'done marked-count cat) 3158 (todo-update-count 'done marked-count cat)
3159 (unless newcat ; Newly added category has no archive. 3159 (unless newcat ; Newly added category has no archive.
3160 (todos-update-count 'archived (- marked-count) cat))) 3160 (todo-update-count 'archived (- marked-count) cat)))
3161 (t 3161 (t
3162 (insert item) 3162 (insert item)
3163 (todos-update-count 'done 1 cat) 3163 (todo-update-count 'done 1 cat)
3164 (unless newcat ; Newly added category has no archive. 3164 (unless newcat ; Newly added category has no archive.
3165 (todos-update-count 'archived -1 cat)))) 3165 (todo-update-count 'archived -1 cat))))
3166 (todos-update-categories-sexp))) 3166 (todo-update-categories-sexp)))
3167 ;; Delete restored items from archive. 3167 ;; Delete restored items from archive.
3168 (when marked 3168 (when marked
3169 (setq item nil) 3169 (setq item nil)
3170 (goto-char (point-min))) 3170 (goto-char (point-min)))
3171 (catch 'done 3171 (catch 'done
3172 (while (not (eobp)) 3172 (while (not (eobp))
3173 (if (or (todos-marked-item-p) item) 3173 (if (or (todo-marked-item-p) item)
3174 (progn 3174 (progn
3175 (todos-remove-item) 3175 (todo-remove-item)
3176 (when item 3176 (when item
3177 (throw 'done (setq item nil)))) 3177 (throw 'done (setq item nil))))
3178 (todos-forward-item)))) 3178 (todo-forward-item))))
3179 (todos-update-count 'done (if marked (- marked-count) -1) cat) 3179 (todo-update-count 'done (if marked (- marked-count) -1) cat)
3180 ;; If that was the last category in the archive, delete the whole file. 3180 ;; If that was the last category in the archive, delete the whole file.
3181 (if (= (length todos-categories) 1) 3181 (if (= (length todo-categories) 1)
3182 (progn 3182 (progn
3183 (delete-file todos-current-todos-file) 3183 (delete-file todo-current-todo-file)
3184 ;; Kill the archive buffer silently. 3184 ;; Kill the archive buffer silently.
3185 (set-buffer-modified-p nil) 3185 (set-buffer-modified-p nil)
3186 (kill-buffer)) 3186 (kill-buffer))
@@ -3188,127 +3188,127 @@ the only category in the archive, the archive file is deleted."
3188 (when (eq (point-min) (point-max)) 3188 (when (eq (point-min) (point-max))
3189 (widen) 3189 (widen)
3190 (let ((beg (re-search-backward 3190 (let ((beg (re-search-backward
3191 (concat "^" (regexp-quote todos-category-beg) cat "$") 3191 (concat "^" (regexp-quote todo-category-beg) cat "$")
3192 nil t)) 3192 nil t))
3193 (end (if (re-search-forward 3193 (end (if (re-search-forward
3194 (concat "^" (regexp-quote todos-category-beg)) 3194 (concat "^" (regexp-quote todo-category-beg))
3195 nil t 2) 3195 nil t 2)
3196 (match-beginning 0) 3196 (match-beginning 0)
3197 (point-max)))) 3197 (point-max))))
3198 (remove-overlays beg end) 3198 (remove-overlays beg end)
3199 (delete-region beg end) 3199 (delete-region beg end)
3200 (setq todos-categories (delete (assoc cat todos-categories) 3200 (setq todo-categories (delete (assoc cat todo-categories)
3201 todos-categories)) 3201 todo-categories))
3202 (todos-update-categories-sexp)))) 3202 (todo-update-categories-sexp))))
3203 ;; Visit category in Todos file and show restored done items. 3203 ;; Visit category in Todo file and show restored done items.
3204 (let ((tfile (buffer-file-name tbuf)) 3204 (let ((tfile (buffer-file-name tbuf))
3205 (todos-show-with-done t)) 3205 (todo-show-with-done t))
3206 (set-window-buffer (selected-window) 3206 (set-window-buffer (selected-window)
3207 (set-buffer (find-file-noselect tfile))) 3207 (set-buffer (find-file-noselect tfile)))
3208 (todos-category-number cat) 3208 (todo-category-number cat)
3209 (todos-category-select) 3209 (todo-category-select)
3210 (message "Items unarchived."))))) 3210 (message "Items unarchived.")))))
3211 3211
3212(defun todos-jump-to-archive-category (&optional file) 3212(defun todo-jump-to-archive-category (&optional file)
3213 "Prompt for a category in a Todos archive and jump to it. 3213 "Prompt for a category in a Todo archive and jump to it.
3214With prefix argument FILE, prompt for an archive and choose (with 3214With prefix argument FILE, prompt for an archive and choose (with
3215TAB completion) a category in it to jump to; otherwise, choose 3215TAB completion) a category in it to jump to; otherwise, choose
3216and jump to any category in the current archive." 3216and jump to any category in the current archive."
3217 (interactive "P") 3217 (interactive "P")
3218 (todos-jump-to-category file 'archive)) 3218 (todo-jump-to-category file 'archive))
3219 3219
3220;; ----------------------------------------------------------------------------- 3220;; -----------------------------------------------------------------------------
3221;;; Displaying and sorting tables of categories 3221;;; Displaying and sorting tables of categories
3222;; ----------------------------------------------------------------------------- 3222;; -----------------------------------------------------------------------------
3223 3223
3224(defcustom todos-categories-category-label "Category" 3224(defcustom todo-categories-category-label "Category"
3225 "Category button label in Todos Categories mode." 3225 "Category button label in Todo Categories mode."
3226 :type 'string 3226 :type 'string
3227 :group 'todos-categories) 3227 :group 'todo-categories)
3228 3228
3229(defcustom todos-categories-todo-label "Todo" 3229(defcustom todo-categories-todo-label "Todo"
3230 "Todo button label in Todos Categories mode." 3230 "Todo button label in Todo Categories mode."
3231 :type 'string 3231 :type 'string
3232 :group 'todos-categories) 3232 :group 'todo-categories)
3233 3233
3234(defcustom todos-categories-diary-label "Diary" 3234(defcustom todo-categories-diary-label "Diary"
3235 "Diary button label in Todos Categories mode." 3235 "Diary button label in Todo Categories mode."
3236 :type 'string 3236 :type 'string
3237 :group 'todos-categories) 3237 :group 'todo-categories)
3238 3238
3239(defcustom todos-categories-done-label "Done" 3239(defcustom todo-categories-done-label "Done"
3240 "Done button label in Todos Categories mode." 3240 "Done button label in Todo Categories mode."
3241 :type 'string 3241 :type 'string
3242 :group 'todos-categories) 3242 :group 'todo-categories)
3243 3243
3244(defcustom todos-categories-archived-label "Archived" 3244(defcustom todo-categories-archived-label "Archived"
3245 "Archived button label in Todos Categories mode." 3245 "Archived button label in Todo Categories mode."
3246 :type 'string 3246 :type 'string
3247 :group 'todos-categories) 3247 :group 'todo-categories)
3248 3248
3249(defcustom todos-categories-totals-label "Totals" 3249(defcustom todo-categories-totals-label "Totals"
3250 "String to label total item counts in Todos Categories mode." 3250 "String to label total item counts in Todo Categories mode."
3251 :type 'string 3251 :type 'string
3252 :group 'todos-categories) 3252 :group 'todo-categories)
3253 3253
3254(defcustom todos-categories-number-separator " | " 3254(defcustom todo-categories-number-separator " | "
3255 "String between number and category in Todos Categories mode. 3255 "String between number and category in Todo Categories mode.
3256This separates the number from the category name in the default 3256This separates the number from the category name in the default
3257categories display according to priority." 3257categories display according to priority."
3258 :type 'string 3258 :type 'string
3259 :group 'todos-categories) 3259 :group 'todo-categories)
3260 3260
3261(defcustom todos-categories-align 'center 3261(defcustom todo-categories-align 'center
3262 "Alignment of category names in Todos Categories mode." 3262 "Alignment of category names in Todo Categories mode."
3263 :type '(radio (const left) (const center) (const right)) 3263 :type '(radio (const left) (const center) (const right))
3264 :group 'todos-categories) 3264 :group 'todo-categories)
3265 3265
3266(defun todos-show-categories-table () 3266(defun todo-show-categories-table ()
3267 "Display a table of the current file's categories and item counts. 3267 "Display a table of the current file's categories and item counts.
3268 3268
3269In the initial display the categories are numbered, indicating 3269In the initial display the categories are numbered, indicating
3270their current order for navigating by \\[todos-forward-category] 3270their current order for navigating by \\[todo-forward-category]
3271and \\[todos-backward-category]. You can persistantly change the 3271and \\[todo-backward-category]. You can persistantly change the
3272order of the category at point by typing 3272order of the category at point by typing
3273\\[todos-set-category-number], \\[todos-raise-category] or 3273\\[todo-set-category-number], \\[todo-raise-category] or
3274\\[todos-lower-category]. 3274\\[todo-lower-category].
3275 3275
3276The labels above the category names and item counts are buttons, 3276The labels above the category names and item counts are buttons,
3277and clicking these changes the display: sorted by category name 3277and clicking these changes the display: sorted by category name
3278or by the respective item counts (alternately descending or 3278or by the respective item counts (alternately descending or
3279ascending). In these displays the categories are not numbered 3279ascending). In these displays the categories are not numbered
3280and \\[todos-set-category-number], \\[todos-raise-category] and 3280and \\[todo-set-category-number], \\[todo-raise-category] and
3281\\[todos-lower-category] are disabled. (Programmatically, the 3281\\[todo-lower-category] are disabled. (Programmatically, the
3282sorting is triggered by passing a non-nil SORTKEY argument.) 3282sorting is triggered by passing a non-nil SORTKEY argument.)
3283 3283
3284In addition, the lines with the category names and item counts 3284In addition, the lines with the category names and item counts
3285are buttonized, and pressing one of these button jumps to the 3285are buttonized, and pressing one of these button jumps to the
3286category in Todos mode (or Todos Archive mode, for categories 3286category in Todo mode (or Todo Archive mode, for categories
3287containing only archived items, provided user option 3287containing only archived items, provided user option
3288`todos-skip-archived-categories' is non-nil. These categories 3288`todo-skip-archived-categories' is non-nil. These categories
3289are shown in `todos-archived-only' face." 3289are shown in `todo-archived-only' face."
3290 (interactive) 3290 (interactive)
3291 (todos-display-categories) 3291 (todo-display-categories)
3292 (let (sortkey) 3292 (let (sortkey)
3293 (todos-update-categories-display sortkey))) 3293 (todo-update-categories-display sortkey)))
3294 3294
3295(defun todos-next-button (n) 3295(defun todo-next-button (n)
3296 "Move point to the Nth next button in the table of categories." 3296 "Move point to the Nth next button in the table of categories."
3297 (interactive "p") 3297 (interactive "p")
3298 (forward-button n 'wrap 'display-message) 3298 (forward-button n 'wrap 'display-message)
3299 (and (bolp) (button-at (point)) 3299 (and (bolp) (button-at (point))
3300 ;; Align with beginning of category label. 3300 ;; Align with beginning of category label.
3301 (forward-char (+ 4 (length todos-categories-number-separator))))) 3301 (forward-char (+ 4 (length todo-categories-number-separator)))))
3302 3302
3303(defun todos-previous-button (n) 3303(defun todo-previous-button (n)
3304 "Move point to the Nth previous button in the table of categories." 3304 "Move point to the Nth previous button in the table of categories."
3305 (interactive "p") 3305 (interactive "p")
3306 (backward-button n 'wrap 'display-message) 3306 (backward-button n 'wrap 'display-message)
3307 (and (bolp) (button-at (point)) 3307 (and (bolp) (button-at (point))
3308 ;; Align with beginning of category label. 3308 ;; Align with beginning of category label.
3309 (forward-char (+ 4 (length todos-categories-number-separator))))) 3309 (forward-char (+ 4 (length todo-categories-number-separator)))))
3310 3310
3311(defun todos-set-category-number (&optional arg) 3311(defun todo-set-category-number (&optional arg)
3312 "Change number of category at point in the table of categories. 3312 "Change number of category at point in the table of categories.
3313 3313
3314With ARG nil, prompt for the new number. Alternatively, the 3314With ARG nil, prompt for the new number. Alternatively, the
@@ -3322,7 +3322,7 @@ decreasing or increasing its number."
3322 ;; on the current line. 3322 ;; on the current line.
3323 (forward-line 0) (skip-chars-forward " ") (number-at-point)))) 3323 (forward-line 0) (skip-chars-forward " ") (number-at-point))))
3324 (when curnum ; Do nothing if we're not on a category line. 3324 (when curnum ; Do nothing if we're not on a category line.
3325 (let* ((maxnum (length todos-categories)) 3325 (let* ((maxnum (length todo-categories))
3326 (prompt (format "Set category priority (1-%d): " maxnum)) 3326 (prompt (format "Set category priority (1-%d): " maxnum))
3327 (col (current-column)) 3327 (col (current-column))
3328 (buffer-read-only nil) 3328 (buffer-read-only nil)
@@ -3342,134 +3342,134 @@ decreasing or increasing its number."
3342 "Choose a different priority than the current one: "))) 3342 "Choose a different priority than the current one: ")))
3343 (unless prompt (setq priority candidate))) 3343 (unless prompt (setq priority candidate)))
3344 (let* ((lower (< curnum priority)) ; Priority is being lowered. 3344 (let* ((lower (< curnum priority)) ; Priority is being lowered.
3345 (head (butlast todos-categories 3345 (head (butlast todo-categories
3346 (apply (if lower 'identity '1+) 3346 (apply (if lower 'identity '1+)
3347 (list (- maxnum priority))))) 3347 (list (- maxnum priority)))))
3348 (tail (nthcdr (apply (if lower 'identity '1-) (list priority)) 3348 (tail (nthcdr (apply (if lower 'identity '1-) (list priority))
3349 todos-categories)) 3349 todo-categories))
3350 ;; Category's name and items counts list. 3350 ;; Category's name and items counts list.
3351 (catcons (nth (1- curnum) todos-categories)) 3351 (catcons (nth (1- curnum) todo-categories))
3352 (todos-categories (nconc head (list catcons) tail)) 3352 (todo-categories (nconc head (list catcons) tail))
3353 newcats) 3353 newcats)
3354 (when lower (setq todos-categories (nreverse todos-categories))) 3354 (when lower (setq todo-categories (nreverse todo-categories)))
3355 (setq todos-categories (delete-dups todos-categories)) 3355 (setq todo-categories (delete-dups todo-categories))
3356 (when lower (setq todos-categories (nreverse todos-categories))) 3356 (when lower (setq todo-categories (nreverse todo-categories)))
3357 (setq newcats todos-categories) 3357 (setq newcats todo-categories)
3358 (kill-buffer) 3358 (kill-buffer)
3359 (with-current-buffer (find-buffer-visiting todos-current-todos-file) 3359 (with-current-buffer (find-buffer-visiting todo-current-todo-file)
3360 (setq todos-categories newcats) 3360 (setq todo-categories newcats)
3361 (todos-update-categories-sexp)) 3361 (todo-update-categories-sexp))
3362 (todos-show-categories-table) 3362 (todo-show-categories-table)
3363 (forward-line (1+ priority)) 3363 (forward-line (1+ priority))
3364 (forward-char col)))))) 3364 (forward-char col))))))
3365 3365
3366(defun todos-raise-category () 3366(defun todo-raise-category ()
3367 "Raise priority of category at point in Todos Categories buffer." 3367 "Raise priority of category at point in Todo Categories buffer."
3368 (interactive) 3368 (interactive)
3369 (todos-set-category-number 'raise)) 3369 (todo-set-category-number 'raise))
3370 3370
3371(defun todos-lower-category () 3371(defun todo-lower-category ()
3372 "Lower priority of category at point in Todos Categories buffer." 3372 "Lower priority of category at point in Todo Categories buffer."
3373 (interactive) 3373 (interactive)
3374 (todos-set-category-number 'lower)) 3374 (todo-set-category-number 'lower))
3375 3375
3376(defun todos-sort-categories-alphabetically-or-numerically () 3376(defun todo-sort-categories-alphabetically-or-numerically ()
3377 "Sort table of categories alphabetically or numerically." 3377 "Sort table of categories alphabetically or numerically."
3378 (interactive) 3378 (interactive)
3379 (save-excursion 3379 (save-excursion
3380 (goto-char (point-min)) 3380 (goto-char (point-min))
3381 (forward-line 2) 3381 (forward-line 2)
3382 (if (member 'alpha todos-descending-counts) 3382 (if (member 'alpha todo-descending-counts)
3383 (progn 3383 (progn
3384 (todos-update-categories-display nil) 3384 (todo-update-categories-display nil)
3385 (setq todos-descending-counts 3385 (setq todo-descending-counts
3386 (delete 'alpha todos-descending-counts))) 3386 (delete 'alpha todo-descending-counts)))
3387 (todos-update-categories-display 'alpha)))) 3387 (todo-update-categories-display 'alpha))))
3388 3388
3389(defun todos-sort-categories-by-todo () 3389(defun todo-sort-categories-by-todo ()
3390 "Sort table of categories by number of todo items." 3390 "Sort table of categories by number of todo items."
3391 (interactive) 3391 (interactive)
3392 (save-excursion 3392 (save-excursion
3393 (goto-char (point-min)) 3393 (goto-char (point-min))
3394 (forward-line 2) 3394 (forward-line 2)
3395 (todos-update-categories-display 'todo))) 3395 (todo-update-categories-display 'todo)))
3396 3396
3397(defun todos-sort-categories-by-diary () 3397(defun todo-sort-categories-by-diary ()
3398 "Sort table of categories by number of diary items." 3398 "Sort table of categories by number of diary items."
3399 (interactive) 3399 (interactive)
3400 (save-excursion 3400 (save-excursion
3401 (goto-char (point-min)) 3401 (goto-char (point-min))
3402 (forward-line 2) 3402 (forward-line 2)
3403 (todos-update-categories-display 'diary))) 3403 (todo-update-categories-display 'diary)))
3404 3404
3405(defun todos-sort-categories-by-done () 3405(defun todo-sort-categories-by-done ()
3406 "Sort table of categories by number of non-archived done items." 3406 "Sort table of categories by number of non-archived done items."
3407 (interactive) 3407 (interactive)
3408 (save-excursion 3408 (save-excursion
3409 (goto-char (point-min)) 3409 (goto-char (point-min))
3410 (forward-line 2) 3410 (forward-line 2)
3411 (todos-update-categories-display 'done))) 3411 (todo-update-categories-display 'done)))
3412 3412
3413(defun todos-sort-categories-by-archived () 3413(defun todo-sort-categories-by-archived ()
3414 "Sort table of categories by number of archived items." 3414 "Sort table of categories by number of archived items."
3415 (interactive) 3415 (interactive)
3416 (save-excursion 3416 (save-excursion
3417 (goto-char (point-min)) 3417 (goto-char (point-min))
3418 (forward-line 2) 3418 (forward-line 2)
3419 (todos-update-categories-display 'archived))) 3419 (todo-update-categories-display 'archived)))
3420 3420
3421(defvar todos-categories-buffer "*Todos Categories*" 3421(defvar todo-categories-buffer "*Todo Categories*"
3422 "Name of buffer in Todos Categories mode.") 3422 "Name of buffer in Todo Categories mode.")
3423 3423
3424(defun todos-longest-category-name-length (categories) 3424(defun todo-longest-category-name-length (categories)
3425 "Return the length of the longest name in list CATEGORIES." 3425 "Return the length of the longest name in list CATEGORIES."
3426 (let ((longest 0)) 3426 (let ((longest 0))
3427 (dolist (c categories longest) 3427 (dolist (c categories longest)
3428 (setq longest (max longest (length c)))))) 3428 (setq longest (max longest (length c))))))
3429 3429
3430(defun todos-adjusted-category-label-length () 3430(defun todo-adjusted-category-label-length ()
3431 "Return adjusted length of category label button. 3431 "Return adjusted length of category label button.
3432The adjustment ensures proper tabular alignment in Todos 3432The adjustment ensures proper tabular alignment in Todo
3433Categories mode." 3433Categories mode."
3434 (let* ((categories (mapcar 'car todos-categories)) 3434 (let* ((categories (mapcar 'car todo-categories))
3435 (longest (todos-longest-category-name-length categories)) 3435 (longest (todo-longest-category-name-length categories))
3436 (catlablen (length todos-categories-category-label)) 3436 (catlablen (length todo-categories-category-label))
3437 (lc-diff (- longest catlablen))) 3437 (lc-diff (- longest catlablen)))
3438 (if (and (natnump lc-diff) (cl-oddp lc-diff)) 3438 (if (and (natnump lc-diff) (cl-oddp lc-diff))
3439 (1+ longest) 3439 (1+ longest)
3440 (max longest catlablen)))) 3440 (max longest catlablen))))
3441 3441
3442(defun todos-padded-string (str) 3442(defun todo-padded-string (str)
3443 "Return category name or label string STR padded with spaces. 3443 "Return category name or label string STR padded with spaces.
3444The placement of the padding is determined by the value of user 3444The placement of the padding is determined by the value of user
3445option `todos-categories-align'." 3445option `todo-categories-align'."
3446 (let* ((len (todos-adjusted-category-label-length)) 3446 (let* ((len (todo-adjusted-category-label-length))
3447 (strlen (length str)) 3447 (strlen (length str))
3448 (strlen-odd (eq (logand strlen 1) 1)) 3448 (strlen-odd (eq (logand strlen 1) 1))
3449 (padding (max 0 (/ (- len strlen) 2))) 3449 (padding (max 0 (/ (- len strlen) 2)))
3450 (padding-left (cond ((eq todos-categories-align 'left) 0) 3450 (padding-left (cond ((eq todo-categories-align 'left) 0)
3451 ((eq todos-categories-align 'center) padding) 3451 ((eq todo-categories-align 'center) padding)
3452 ((eq todos-categories-align 'right) 3452 ((eq todo-categories-align 'right)
3453 (if strlen-odd (1+ (* padding 2)) (* padding 2))))) 3453 (if strlen-odd (1+ (* padding 2)) (* padding 2)))))
3454 (padding-right (cond ((eq todos-categories-align 'left) 3454 (padding-right (cond ((eq todo-categories-align 'left)
3455 (if strlen-odd (1+ (* padding 2)) (* padding 2))) 3455 (if strlen-odd (1+ (* padding 2)) (* padding 2)))
3456 ((eq todos-categories-align 'center) 3456 ((eq todo-categories-align 'center)
3457 (if strlen-odd (1+ padding) padding)) 3457 (if strlen-odd (1+ padding) padding))
3458 ((eq todos-categories-align 'right) 0)))) 3458 ((eq todo-categories-align 'right) 0))))
3459 (concat (make-string padding-left 32) str (make-string padding-right 32)))) 3459 (concat (make-string padding-left 32) str (make-string padding-right 32))))
3460 3460
3461(defvar todos-descending-counts nil 3461(defvar todo-descending-counts nil
3462 "List of keys for category counts sorted in descending order.") 3462 "List of keys for category counts sorted in descending order.")
3463 3463
3464(defun todos-sort (list &optional key) 3464(defun todo-sort (list &optional key)
3465 "Return a copy of LIST, possibly sorted according to KEY." 3465 "Return a copy of LIST, possibly sorted according to KEY."
3466 (let* ((l (copy-sequence list)) 3466 (let* ((l (copy-sequence list))
3467 (fn (if (eq key 'alpha) 3467 (fn (if (eq key 'alpha)
3468 (lambda (x) (upcase x)) ; Alphabetize case insensitively. 3468 (lambda (x) (upcase x)) ; Alphabetize case insensitively.
3469 (lambda (x) (todos-get-count key x)))) 3469 (lambda (x) (todo-get-count key x))))
3470 ;; Keep track of whether the last sort by key was descending or 3470 ;; Keep track of whether the last sort by key was descending or
3471 ;; ascending. 3471 ;; ascending.
3472 (descending (member key todos-descending-counts)) 3472 (descending (member key todo-descending-counts))
3473 (cmp (if (eq key 'alpha) 3473 (cmp (if (eq key 'alpha)
3474 'string< 3474 'string<
3475 (if descending '< '>))) 3475 (if descending '< '>)))
@@ -3480,37 +3480,37 @@ option `todos-categories-align'."
3480 (setq l (sort l pred)) 3480 (setq l (sort l pred))
3481 ;; Switch between descending and ascending sort order. 3481 ;; Switch between descending and ascending sort order.
3482 (if descending 3482 (if descending
3483 (setq todos-descending-counts 3483 (setq todo-descending-counts
3484 (delete key todos-descending-counts)) 3484 (delete key todo-descending-counts))
3485 (push key todos-descending-counts))) 3485 (push key todo-descending-counts)))
3486 l)) 3486 l))
3487 3487
3488(defun todos-display-sorted (type) 3488(defun todo-display-sorted (type)
3489 "Keep point on the TYPE count sorting button just clicked." 3489 "Keep point on the TYPE count sorting button just clicked."
3490 (let ((opoint (point))) 3490 (let ((opoint (point)))
3491 (todos-update-categories-display type) 3491 (todo-update-categories-display type)
3492 (goto-char opoint))) 3492 (goto-char opoint)))
3493 3493
3494(defun todos-label-to-key (label) 3494(defun todo-label-to-key (label)
3495 "Return symbol for sort key associated with LABEL." 3495 "Return symbol for sort key associated with LABEL."
3496 (let (key) 3496 (let (key)
3497 (cond ((string= label todos-categories-category-label) 3497 (cond ((string= label todo-categories-category-label)
3498 (setq key 'alpha)) 3498 (setq key 'alpha))
3499 ((string= label todos-categories-todo-label) 3499 ((string= label todo-categories-todo-label)
3500 (setq key 'todo)) 3500 (setq key 'todo))
3501 ((string= label todos-categories-diary-label) 3501 ((string= label todo-categories-diary-label)
3502 (setq key 'diary)) 3502 (setq key 'diary))
3503 ((string= label todos-categories-done-label) 3503 ((string= label todo-categories-done-label)
3504 (setq key 'done)) 3504 (setq key 'done))
3505 ((string= label todos-categories-archived-label) 3505 ((string= label todo-categories-archived-label)
3506 (setq key 'archived))) 3506 (setq key 'archived)))
3507 key)) 3507 key))
3508 3508
3509(defun todos-insert-sort-button (label) 3509(defun todo-insert-sort-button (label)
3510 "Insert button for displaying categories sorted by item counts. 3510 "Insert button for displaying categories sorted by item counts.
3511LABEL determines which type of count is sorted." 3511LABEL determines which type of count is sorted."
3512 (let* ((str (if (string= label todos-categories-category-label) 3512 (let* ((str (if (string= label todo-categories-category-label)
3513 (todos-padded-string label) 3513 (todo-padded-string label)
3514 label)) 3514 label))
3515 (beg (point)) 3515 (beg (point))
3516 (end (+ beg (length str))) 3516 (end (+ beg (length str)))
@@ -3518,137 +3518,137 @@ LABEL determines which type of count is sorted."
3518 (insert-button str 'face nil 3518 (insert-button str 'face nil
3519 'action 3519 'action
3520 `(lambda (button) 3520 `(lambda (button)
3521 (let ((key (todos-label-to-key ,label))) 3521 (let ((key (todo-label-to-key ,label)))
3522 (if (and (member key todos-descending-counts) 3522 (if (and (member key todo-descending-counts)
3523 (eq key 'alpha)) 3523 (eq key 'alpha))
3524 (progn 3524 (progn
3525 ;; If display is alphabetical, switch back to 3525 ;; If display is alphabetical, switch back to
3526 ;; category priority order. 3526 ;; category priority order.
3527 (todos-display-sorted nil) 3527 (todo-display-sorted nil)
3528 (setq todos-descending-counts 3528 (setq todo-descending-counts
3529 (delete key todos-descending-counts))) 3529 (delete key todo-descending-counts)))
3530 (todos-display-sorted key))))) 3530 (todo-display-sorted key)))))
3531 (setq ov (make-overlay beg end)) 3531 (setq ov (make-overlay beg end))
3532 (overlay-put ov 'face 'todos-button))) 3532 (overlay-put ov 'face 'todo-button)))
3533 3533
3534(defun todos-total-item-counts () 3534(defun todo-total-item-counts ()
3535 "Return a list of total item counts for the current file." 3535 "Return a list of total item counts for the current file."
3536 (mapcar (lambda (i) (apply '+ (mapcar (lambda (l) (aref l i)) 3536 (mapcar (lambda (i) (apply '+ (mapcar (lambda (l) (aref l i))
3537 (mapcar 'cdr todos-categories)))) 3537 (mapcar 'cdr todo-categories))))
3538 (list 0 1 2 3))) 3538 (list 0 1 2 3)))
3539 3539
3540(defvar todos-categories-category-number 0 3540(defvar todo-categories-category-number 0
3541 "Variable for numbering categories in Todos Categories mode.") 3541 "Variable for numbering categories in Todo Categories mode.")
3542 3542
3543(defun todos-insert-category-line (cat &optional nonum) 3543(defun todo-insert-category-line (cat &optional nonum)
3544 "Insert button with category CAT's name and item counts. 3544 "Insert button with category CAT's name and item counts.
3545With non-nil argument NONUM show only these; otherwise, insert a 3545With non-nil argument NONUM show only these; otherwise, insert a
3546number in front of the button indicating the category's priority. 3546number in front of the button indicating the category's priority.
3547The number and the category name are separated by the string 3547The number and the category name are separated by the string
3548which is the value of the user option 3548which is the value of the user option
3549`todos-categories-number-separator'." 3549`todo-categories-number-separator'."
3550 (let ((archive (member todos-current-todos-file todos-archives)) 3550 (let ((archive (member todo-current-todo-file todo-archives))
3551 (num todos-categories-category-number) 3551 (num todo-categories-category-number)
3552 (str (todos-padded-string cat)) 3552 (str (todo-padded-string cat))
3553 (opoint (point))) 3553 (opoint (point)))
3554 (setq num (1+ num) todos-categories-category-number num) 3554 (setq num (1+ num) todo-categories-category-number num)
3555 (insert-button 3555 (insert-button
3556 (concat (if nonum 3556 (concat (if nonum
3557 (make-string (+ 4 (length todos-categories-number-separator)) 3557 (make-string (+ 4 (length todo-categories-number-separator))
3558 32) 3558 32)
3559 (format " %3d%s" num todos-categories-number-separator)) 3559 (format " %3d%s" num todo-categories-number-separator))
3560 str 3560 str
3561 (mapconcat (lambda (elt) 3561 (mapconcat (lambda (elt)
3562 (concat 3562 (concat
3563 (make-string (1+ (/ (length (car elt)) 2)) 32) ; label 3563 (make-string (1+ (/ (length (car elt)) 2)) 32) ; label
3564 (format "%3d" (todos-get-count (cdr elt) cat)) ; count 3564 (format "%3d" (todo-get-count (cdr elt) cat)) ; count
3565 ;; Add an extra space if label length is odd. 3565 ;; Add an extra space if label length is odd.
3566 (when (cl-oddp (length (car elt))) " "))) 3566 (when (cl-oddp (length (car elt))) " ")))
3567 (if archive 3567 (if archive
3568 (list (cons todos-categories-done-label 'done)) 3568 (list (cons todo-categories-done-label 'done))
3569 (list (cons todos-categories-todo-label 'todo) 3569 (list (cons todo-categories-todo-label 'todo)
3570 (cons todos-categories-diary-label 'diary) 3570 (cons todo-categories-diary-label 'diary)
3571 (cons todos-categories-done-label 'done) 3571 (cons todo-categories-done-label 'done)
3572 (cons todos-categories-archived-label 3572 (cons todo-categories-archived-label
3573 'archived))) 3573 'archived)))
3574 "") 3574 "")
3575 " ") ; Make highlighting on last column look better. 3575 " ") ; Make highlighting on last column look better.
3576 'face (if (and todos-skip-archived-categories 3576 'face (if (and todo-skip-archived-categories
3577 (zerop (todos-get-count 'todo cat)) 3577 (zerop (todo-get-count 'todo cat))
3578 (zerop (todos-get-count 'done cat)) 3578 (zerop (todo-get-count 'done cat))
3579 (not (zerop (todos-get-count 'archived cat)))) 3579 (not (zerop (todo-get-count 'archived cat))))
3580 'todos-archived-only 3580 'todo-archived-only
3581 nil) 3581 nil)
3582 'action `(lambda (button) (let ((buf (current-buffer))) 3582 'action `(lambda (button) (let ((buf (current-buffer)))
3583 (todos-jump-to-category nil ,cat) 3583 (todo-jump-to-category nil ,cat)
3584 (kill-buffer buf)))) 3584 (kill-buffer buf))))
3585 ;; Highlight the sorted count column. 3585 ;; Highlight the sorted count column.
3586 (let* ((beg (+ opoint 7 (length str))) 3586 (let* ((beg (+ opoint 7 (length str)))
3587 end ovl) 3587 end ovl)
3588 (cond ((eq nonum 'todo) 3588 (cond ((eq nonum 'todo)
3589 (setq beg (+ beg 1 (/ (length todos-categories-todo-label) 2)))) 3589 (setq beg (+ beg 1 (/ (length todo-categories-todo-label) 2))))
3590 ((eq nonum 'diary) 3590 ((eq nonum 'diary)
3591 (setq beg (+ beg 1 (length todos-categories-todo-label) 3591 (setq beg (+ beg 1 (length todo-categories-todo-label)
3592 2 (/ (length todos-categories-diary-label) 2)))) 3592 2 (/ (length todo-categories-diary-label) 2))))
3593 ((eq nonum 'done) 3593 ((eq nonum 'done)
3594 (setq beg (+ beg 1 (length todos-categories-todo-label) 3594 (setq beg (+ beg 1 (length todo-categories-todo-label)
3595 2 (length todos-categories-diary-label) 3595 2 (length todo-categories-diary-label)
3596 2 (/ (length todos-categories-done-label) 2)))) 3596 2 (/ (length todo-categories-done-label) 2))))
3597 ((eq nonum 'archived) 3597 ((eq nonum 'archived)
3598 (setq beg (+ beg 1 (length todos-categories-todo-label) 3598 (setq beg (+ beg 1 (length todo-categories-todo-label)
3599 2 (length todos-categories-diary-label) 3599 2 (length todo-categories-diary-label)
3600 2 (length todos-categories-done-label) 3600 2 (length todo-categories-done-label)
3601 2 (/ (length todos-categories-archived-label) 2))))) 3601 2 (/ (length todo-categories-archived-label) 2)))))
3602 (unless (= beg (+ opoint 7 (length str))) ; Don't highlight categories. 3602 (unless (= beg (+ opoint 7 (length str))) ; Don't highlight categories.
3603 (setq end (+ beg 4)) 3603 (setq end (+ beg 4))
3604 (setq ovl (make-overlay beg end)) 3604 (setq ovl (make-overlay beg end))
3605 (overlay-put ovl 'face 'todos-sorted-column))) 3605 (overlay-put ovl 'face 'todo-sorted-column)))
3606 (newline))) 3606 (newline)))
3607 3607
3608(defun todos-display-categories () 3608(defun todo-display-categories ()
3609 "Prepare buffer for displaying table of categories and item counts." 3609 "Prepare buffer for displaying table of categories and item counts."
3610 (unless (eq major-mode 'todos-categories-mode) 3610 (unless (eq major-mode 'todo-categories-mode)
3611 (setq todos-global-current-todos-file 3611 (setq todo-global-current-todo-file
3612 (or todos-current-todos-file 3612 (or todo-current-todo-file
3613 (todos-absolute-file-name todos-default-todos-file))) 3613 (todo-absolute-file-name todo-default-todo-file)))
3614 (set-window-buffer (selected-window) 3614 (set-window-buffer (selected-window)
3615 (set-buffer (get-buffer-create todos-categories-buffer))) 3615 (set-buffer (get-buffer-create todo-categories-buffer)))
3616 (kill-all-local-variables) 3616 (kill-all-local-variables)
3617 (todos-categories-mode) 3617 (todo-categories-mode)
3618 (let ((archive (member todos-current-todos-file todos-archives)) 3618 (let ((archive (member todo-current-todo-file todo-archives))
3619 buffer-read-only) 3619 buffer-read-only)
3620 (erase-buffer) 3620 (erase-buffer)
3621 (insert (format (concat "Category counts for Todos " 3621 (insert (format (concat "Category counts for Todo "
3622 (if archive "archive" "file") 3622 (if archive "archive" "file")
3623 " \"%s\".") 3623 " \"%s\".")
3624 (todos-short-file-name todos-current-todos-file))) 3624 (todo-short-file-name todo-current-todo-file)))
3625 (newline 2) 3625 (newline 2)
3626 ;; Make space for the column of category numbers. 3626 ;; Make space for the column of category numbers.
3627 (insert (make-string (+ 4 (length todos-categories-number-separator)) 32)) 3627 (insert (make-string (+ 4 (length todo-categories-number-separator)) 32))
3628 ;; Add the category and item count buttons (if this is the list of 3628 ;; Add the category and item count buttons (if this is the list of
3629 ;; categories in an archive, show only done item counts). 3629 ;; categories in an archive, show only done item counts).
3630 (todos-insert-sort-button todos-categories-category-label) 3630 (todo-insert-sort-button todo-categories-category-label)
3631 (if archive 3631 (if archive
3632 (progn 3632 (progn
3633 (insert (make-string 3 32)) 3633 (insert (make-string 3 32))
3634 (todos-insert-sort-button todos-categories-done-label)) 3634 (todo-insert-sort-button todo-categories-done-label))
3635 (insert (make-string 3 32)) 3635 (insert (make-string 3 32))
3636 (todos-insert-sort-button todos-categories-todo-label) 3636 (todo-insert-sort-button todo-categories-todo-label)
3637 (insert (make-string 2 32)) 3637 (insert (make-string 2 32))
3638 (todos-insert-sort-button todos-categories-diary-label) 3638 (todo-insert-sort-button todo-categories-diary-label)
3639 (insert (make-string 2 32)) 3639 (insert (make-string 2 32))
3640 (todos-insert-sort-button todos-categories-done-label) 3640 (todo-insert-sort-button todo-categories-done-label)
3641 (insert (make-string 2 32)) 3641 (insert (make-string 2 32))
3642 (todos-insert-sort-button todos-categories-archived-label)) 3642 (todo-insert-sort-button todo-categories-archived-label))
3643 (newline 2)))) 3643 (newline 2))))
3644 3644
3645(defun todos-update-categories-display (sortkey) 3645(defun todo-update-categories-display (sortkey)
3646 "Populate table of categories and sort by SORTKEY." 3646 "Populate table of categories and sort by SORTKEY."
3647 (let* ((cats0 todos-categories) 3647 (let* ((cats0 todo-categories)
3648 (cats (todos-sort cats0 sortkey)) 3648 (cats (todo-sort cats0 sortkey))
3649 (archive (member todos-current-todos-file todos-archives)) 3649 (archive (member todo-current-todo-file todo-archives))
3650 (todos-categories-category-number 0) 3650 (todo-categories-category-number 0)
3651 ;; Find start of Category button if we just entered Todos Categories 3651 ;; Find start of Category button if we just entered Todo Categories
3652 ;; mode. 3652 ;; mode.
3653 (pt (if (eq (point) (point-max)) 3653 (pt (if (eq (point) (point-max))
3654 (save-excursion 3654 (save-excursion
@@ -3660,25 +3660,25 @@ which is the value of the user option
3660 (delete-region (point) (point-max)) 3660 (delete-region (point) (point-max))
3661 ;; Fill in the table with buttonized lines, each showing a category and 3661 ;; Fill in the table with buttonized lines, each showing a category and
3662 ;; its item counts. 3662 ;; its item counts.
3663 (mapc (lambda (cat) (todos-insert-category-line cat sortkey)) 3663 (mapc (lambda (cat) (todo-insert-category-line cat sortkey))
3664 (mapcar 'car cats)) 3664 (mapcar 'car cats))
3665 (newline) 3665 (newline)
3666 ;; Add a line showing item count totals. 3666 ;; Add a line showing item count totals.
3667 (insert (make-string (+ 4 (length todos-categories-number-separator)) 32) 3667 (insert (make-string (+ 4 (length todo-categories-number-separator)) 32)
3668 (todos-padded-string todos-categories-totals-label) 3668 (todo-padded-string todo-categories-totals-label)
3669 (mapconcat 3669 (mapconcat
3670 (lambda (elt) 3670 (lambda (elt)
3671 (concat 3671 (concat
3672 (make-string (1+ (/ (length (car elt)) 2)) 32) 3672 (make-string (1+ (/ (length (car elt)) 2)) 32)
3673 (format "%3d" (nth (cdr elt) (todos-total-item-counts))) 3673 (format "%3d" (nth (cdr elt) (todo-total-item-counts)))
3674 ;; Add an extra space if label length is odd. 3674 ;; Add an extra space if label length is odd.
3675 (when (cl-oddp (length (car elt))) " "))) 3675 (when (cl-oddp (length (car elt))) " ")))
3676 (if archive 3676 (if archive
3677 (list (cons todos-categories-done-label 2)) 3677 (list (cons todo-categories-done-label 2))
3678 (list (cons todos-categories-todo-label 0) 3678 (list (cons todo-categories-todo-label 0)
3679 (cons todos-categories-diary-label 1) 3679 (cons todo-categories-diary-label 1)
3680 (cons todos-categories-done-label 2) 3680 (cons todo-categories-done-label 2)
3681 (cons todos-categories-archived-label 3))) 3681 (cons todo-categories-archived-label 3)))
3682 "")) 3682 ""))
3683 ;; Put cursor on Category button initially. 3683 ;; Put cursor on Category button initially.
3684 (if pt (goto-char pt)) 3684 (if pt (goto-char pt))
@@ -3688,11 +3688,11 @@ which is the value of the user option
3688;;; Searching and item filtering 3688;;; Searching and item filtering
3689;; ----------------------------------------------------------------------------- 3689;; -----------------------------------------------------------------------------
3690 3690
3691(defun todos-search () 3691(defun todo-search ()
3692 "Search for a regular expression in this Todos file. 3692 "Search for a regular expression in this Todo file.
3693The search runs through the whole file and encompasses all and 3693The search runs through the whole file and encompasses all and
3694only todo and done items; it excludes category names. Multiple 3694only todo and done items; it excludes category names. Multiple
3695matches are shown sequentially, highlighted in `todos-search' 3695matches are shown sequentially, highlighted in `todo-search'
3696face." 3696face."
3697 (interactive) 3697 (interactive)
3698 (let ((regex (read-from-minibuffer "Enter a search string (regexp): ")) 3698 (let ((regex (read-from-minibuffer "Enter a search string (regexp): "))
@@ -3704,7 +3704,7 @@ face."
3704 (setq match (re-search-forward regex nil t)) 3704 (setq match (re-search-forward regex nil t))
3705 (goto-char (line-beginning-position)) 3705 (goto-char (line-beginning-position))
3706 (unless (or (equal (point) 1) 3706 (unless (or (equal (point) 1)
3707 (looking-at (concat "^" (regexp-quote todos-category-beg)))) 3707 (looking-at (concat "^" (regexp-quote todo-category-beg))))
3708 (if match (push match matches))) 3708 (if match (push match matches)))
3709 (forward-line)) 3709 (forward-line))
3710 (setq matches (reverse matches)) 3710 (setq matches (reverse matches))
@@ -3713,22 +3713,22 @@ face."
3713 (while matches 3713 (while matches
3714 (setq match (pop matches)) 3714 (setq match (pop matches))
3715 (goto-char match) 3715 (goto-char match)
3716 (todos-item-start) 3716 (todo-item-start)
3717 (when (looking-at todos-done-string-start) 3717 (when (looking-at todo-done-string-start)
3718 (setq in-done t)) 3718 (setq in-done t))
3719 (re-search-backward (concat "^" (regexp-quote todos-category-beg) 3719 (re-search-backward (concat "^" (regexp-quote todo-category-beg)
3720 "\\(.*\\)\n") nil t) 3720 "\\(.*\\)\n") nil t)
3721 (setq cat (match-string-no-properties 1)) 3721 (setq cat (match-string-no-properties 1))
3722 (todos-category-number cat) 3722 (todo-category-number cat)
3723 (todos-category-select) 3723 (todo-category-select)
3724 (if in-done 3724 (if in-done
3725 (unless todos-show-with-done (todos-toggle-view-done-items))) 3725 (unless todo-show-with-done (todo-toggle-view-done-items)))
3726 (goto-char match) 3726 (goto-char match)
3727 (setq ov (make-overlay (- (point) (length regex)) (point))) 3727 (setq ov (make-overlay (- (point) (length regex)) (point)))
3728 (overlay-put ov 'face 'todos-search) 3728 (overlay-put ov 'face 'todo-search)
3729 (when matches 3729 (when matches
3730 (setq mlen (length matches)) 3730 (setq mlen (length matches))
3731 (if (todos-y-or-n-p 3731 (if (todo-y-or-n-p
3732 (if (> mlen 1) 3732 (if (> mlen 1)
3733 (format "There are %d more matches; go to next match? " 3733 (format "There are %d more matches; go to next match? "
3734 mlen) 3734 mlen)
@@ -3739,190 +3739,190 @@ face."
3739 mlen) 3739 mlen)
3740 "There is one more match.")))))) 3740 "There is one more match."))))))
3741 (setq msg "There are no more matches.")) 3741 (setq msg "There are no more matches."))
3742 (todos-category-select) 3742 (todo-category-select)
3743 (goto-char opoint) 3743 (goto-char opoint)
3744 (message "No match for \"%s\"" regex)) 3744 (message "No match for \"%s\"" regex))
3745 (when msg 3745 (when msg
3746 (if (todos-y-or-n-p (concat msg "\nUnhighlight matches? ")) 3746 (if (todo-y-or-n-p (concat msg "\nUnhighlight matches? "))
3747 (todos-clear-matches) 3747 (todo-clear-matches)
3748 (message "You can unhighlight the matches later by typing %s" 3748 (message "You can unhighlight the matches later by typing %s"
3749 (key-description (car (where-is-internal 3749 (key-description (car (where-is-internal
3750 'todos-clear-matches)))))))) 3750 'todo-clear-matches))))))))
3751 3751
3752(defun todos-clear-matches () 3752(defun todo-clear-matches ()
3753 "Remove highlighting on matches found by todos-search." 3753 "Remove highlighting on matches found by todo-search."
3754 (interactive) 3754 (interactive)
3755 (remove-overlays 1 (1+ (buffer-size)) 'face 'todos-search)) 3755 (remove-overlays 1 (1+ (buffer-size)) 'face 'todo-search))
3756 3756
3757(defcustom todos-top-priorities-overrides nil 3757(defcustom todo-top-priorities-overrides nil
3758 "List of rules specifying number of top priority items to show. 3758 "List of rules specifying number of top priority items to show.
3759These rules override `todos-top-priorities' on invocations of 3759These rules override `todo-top-priorities' on invocations of
3760`\\[todos-filter-top-priorities]' and 3760`\\[todo-filter-top-priorities]' and
3761`\\[todos-filter-top-priorities-multifile]'. Each rule is a list 3761`\\[todo-filter-top-priorities-multifile]'. Each rule is a list
3762of the form (FILE NUM ALIST), where FILE is a member of 3762of the form (FILE NUM ALIST), where FILE is a member of
3763`todos-files', NUM is a number specifying the default number of 3763`todo-files', NUM is a number specifying the default number of
3764top priority items for each category in that file, and ALIST, 3764top priority items for each category in that file, and ALIST,
3765when non-nil, consists of conses of a category name in FILE and a 3765when non-nil, consists of conses of a category name in FILE and a
3766number specifying the default number of top priority items in 3766number specifying the default number of top priority items in
3767that category, which overrides NUM. 3767that category, which overrides NUM.
3768 3768
3769This variable should be set interactively by 3769This variable should be set interactively by
3770`\\[todos-set-top-priorities-in-file]' or 3770`\\[todo-set-top-priorities-in-file]' or
3771`\\[todos-set-top-priorities-in-category]'." 3771`\\[todo-set-top-priorities-in-category]'."
3772 :type 'sexp 3772 :type 'sexp
3773 :group 'todos-filtered) 3773 :group 'todo-filtered)
3774 3774
3775(defcustom todos-top-priorities 1 3775(defcustom todo-top-priorities 1
3776 "Default number of top priorities shown by `todos-filter-top-priorities'." 3776 "Default number of top priorities shown by `todo-filter-top-priorities'."
3777 :type 'integer 3777 :type 'integer
3778 :group 'todos-filtered) 3778 :group 'todo-filtered)
3779 3779
3780(defcustom todos-filter-files nil 3780(defcustom todo-filter-files nil
3781 "List of default files for multifile item filtering." 3781 "List of default files for multifile item filtering."
3782 :type `(set ,@(mapcar (lambda (f) (list 'const f)) 3782 :type `(set ,@(mapcar (lambda (f) (list 'const f))
3783 (mapcar 'todos-short-file-name 3783 (mapcar 'todo-short-file-name
3784 (funcall todos-files-function)))) 3784 (funcall todo-files-function))))
3785 :group 'todos-filtered) 3785 :group 'todo-filtered)
3786 3786
3787(defcustom todos-filter-done-items nil 3787(defcustom todo-filter-done-items nil
3788 "Non-nil to include done items when processing regexp filters. 3788 "Non-nil to include done items when processing regexp filters.
3789Done items from corresponding archive files are also included." 3789Done items from corresponding archive files are also included."
3790 :type 'boolean 3790 :type 'boolean
3791 :group 'todos-filtered) 3791 :group 'todo-filtered)
3792 3792
3793(defun todos-set-top-priorities-in-file () 3793(defun todo-set-top-priorities-in-file ()
3794 "Set number of top priorities for this file. 3794 "Set number of top priorities for this file.
3795See `todos-set-top-priorities' for more details." 3795See `todo-set-top-priorities' for more details."
3796 (interactive) 3796 (interactive)
3797 (todos-set-top-priorities)) 3797 (todo-set-top-priorities))
3798 3798
3799(defun todos-set-top-priorities-in-category () 3799(defun todo-set-top-priorities-in-category ()
3800 "Set number of top priorities for this category. 3800 "Set number of top priorities for this category.
3801See `todos-set-top-priorities' for more details." 3801See `todo-set-top-priorities' for more details."
3802 (interactive) 3802 (interactive)
3803 (todos-set-top-priorities t)) 3803 (todo-set-top-priorities t))
3804 3804
3805(defun todos-filter-top-priorities (&optional arg) 3805(defun todo-filter-top-priorities (&optional arg)
3806 "Display a list of top priority items from different categories. 3806 "Display a list of top priority items from different categories.
3807The categories can be any of those in the current Todos file. 3807The categories can be any of those in the current Todo file.
3808 3808
3809With numerical prefix ARG show at most ARG top priority items 3809With numerical prefix ARG show at most ARG top priority items
3810from each category. With `C-u' as prefix argument show the 3810from each category. With `C-u' as prefix argument show the
3811numbers of top priority items specified by category in 3811numbers of top priority items specified by category in
3812`todos-top-priorities-overrides', if this has an entry for the file(s); 3812`todo-top-priorities-overrides', if this has an entry for the file(s);
3813otherwise show `todos-top-priorities' items per category in the 3813otherwise show `todo-top-priorities' items per category in the
3814file(s). With no prefix argument, if a top priorities file for 3814file(s). With no prefix argument, if a top priorities file for
3815the current Todos file has previously been saved (see 3815the current Todo file has previously been saved (see
3816`todos-save-filtered-items-buffer'), visit this file; if there is 3816`todo-save-filtered-items-buffer'), visit this file; if there is
3817no such file, build the list as with prefix argument `C-u'. 3817no such file, build the list as with prefix argument `C-u'.
3818 3818
3819 The prefix ARG regulates how many top priorities from 3819 The prefix ARG regulates how many top priorities from
3820each category to show, as described above." 3820each category to show, as described above."
3821 (interactive "P") 3821 (interactive "P")
3822 (todos-filter-items 'top arg)) 3822 (todo-filter-items 'top arg))
3823 3823
3824(defun todos-filter-top-priorities-multifile (&optional arg) 3824(defun todo-filter-top-priorities-multifile (&optional arg)
3825 "Display a list of top priority items from different categories. 3825 "Display a list of top priority items from different categories.
3826The categories are a subset of the categories in the files listed 3826The categories are a subset of the categories in the files listed
3827in `todos-filter-files', or if this nil, in the files chosen from 3827in `todo-filter-files', or if this nil, in the files chosen from
3828a file selection dialog that pops up in this case. 3828a file selection dialog that pops up in this case.
3829 3829
3830With numerical prefix ARG show at most ARG top priority items 3830With numerical prefix ARG show at most ARG top priority items
3831from each category in each file. With `C-u' as prefix argument 3831from each category in each file. With `C-u' as prefix argument
3832show the numbers of top priority items specified in 3832show the numbers of top priority items specified in
3833`todos-top-priorities-overrides', if this is non-nil; otherwise show 3833`todo-top-priorities-overrides', if this is non-nil; otherwise show
3834`todos-top-priorities' items per category. With no prefix 3834`todo-top-priorities' items per category. With no prefix
3835argument, if a top priorities file for the chosen Todos files 3835argument, if a top priorities file for the chosen Todo files
3836exists (see `todos-save-filtered-items-buffer'), visit this file; 3836exists (see `todo-save-filtered-items-buffer'), visit this file;
3837if there is no such file, do the same as with prefix argument 3837if there is no such file, do the same as with prefix argument
3838`C-u'." 3838`C-u'."
3839 (interactive "P") 3839 (interactive "P")
3840 (todos-filter-items 'top arg t)) 3840 (todo-filter-items 'top arg t))
3841 3841
3842(defun todos-filter-diary-items (&optional arg) 3842(defun todo-filter-diary-items (&optional arg)
3843 "Display a list of todo diary items from different categories. 3843 "Display a list of todo diary items from different categories.
3844The categories can be any of those in the current Todos file. 3844The categories can be any of those in the current Todo file.
3845 3845
3846Called with no prefix ARG, if a diary items file for the current 3846Called with no prefix ARG, if a diary items file for the current
3847Todos file has previously been saved (see 3847Todo file has previously been saved (see
3848`todos-save-filtered-items-buffer'), visit this file; if there is 3848`todo-save-filtered-items-buffer'), visit this file; if there is
3849no such file, build the list of diary items. Called with a 3849no such file, build the list of diary items. Called with a
3850prefix argument, build the list even if there is a saved file of 3850prefix argument, build the list even if there is a saved file of
3851diary items." 3851diary items."
3852 (interactive "P") 3852 (interactive "P")
3853 (todos-filter-items 'diary arg)) 3853 (todo-filter-items 'diary arg))
3854 3854
3855(defun todos-filter-diary-items-multifile (&optional arg) 3855(defun todo-filter-diary-items-multifile (&optional arg)
3856 "Display a list of todo diary items from different categories. 3856 "Display a list of todo diary items from different categories.
3857The categories are a subset of the categories in the files listed 3857The categories are a subset of the categories in the files listed
3858in `todos-filter-files', or if this nil, in the files chosen from 3858in `todo-filter-files', or if this nil, in the files chosen from
3859a file selection dialog that pops up in this case. 3859a file selection dialog that pops up in this case.
3860 3860
3861Called with no prefix ARG, if a diary items file for the chosen 3861Called with no prefix ARG, if a diary items file for the chosen
3862Todos files has previously been saved (see 3862Todo files has previously been saved (see
3863`todos-save-filtered-items-buffer'), visit this file; if there is 3863`todo-save-filtered-items-buffer'), visit this file; if there is
3864no such file, build the list of diary items. Called with a 3864no such file, build the list of diary items. Called with a
3865prefix argument, build the list even if there is a saved file of 3865prefix argument, build the list even if there is a saved file of
3866diary items." 3866diary items."
3867 (interactive "P") 3867 (interactive "P")
3868 (todos-filter-items 'diary arg t)) 3868 (todo-filter-items 'diary arg t))
3869 3869
3870(defun todos-filter-regexp-items (&optional arg) 3870(defun todo-filter-regexp-items (&optional arg)
3871 "Prompt for a regular expression and display items that match it. 3871 "Prompt for a regular expression and display items that match it.
3872The matches can be from any categories in the current Todos file 3872The matches can be from any categories in the current Todo file
3873and with non-nil option `todos-filter-done-items', can include 3873and with non-nil option `todo-filter-done-items', can include
3874not only todo items but also done items, including those in 3874not only todo items but also done items, including those in
3875Archive files. 3875Archive files.
3876 3876
3877Called with no prefix ARG, if a regexp items file for the current 3877Called with no prefix ARG, if a regexp items file for the current
3878Todos file has previously been saved (see 3878Todo file has previously been saved (see
3879`todos-save-filtered-items-buffer'), visit this file; if there is 3879`todo-save-filtered-items-buffer'), visit this file; if there is
3880no such file, build the list of regexp items. Called with a 3880no such file, build the list of regexp items. Called with a
3881prefix argument, build the list even if there is a saved file of 3881prefix argument, build the list even if there is a saved file of
3882regexp items." 3882regexp items."
3883 (interactive "P") 3883 (interactive "P")
3884 (todos-filter-items 'regexp arg)) 3884 (todo-filter-items 'regexp arg))
3885 3885
3886(defun todos-filter-regexp-items-multifile (&optional arg) 3886(defun todo-filter-regexp-items-multifile (&optional arg)
3887 "Prompt for a regular expression and display items that match it. 3887 "Prompt for a regular expression and display items that match it.
3888The matches can be from any categories in the files listed in 3888The matches can be from any categories in the files listed in
3889`todos-filter-files', or if this nil, in the files chosen from a 3889`todo-filter-files', or if this nil, in the files chosen from a
3890file selection dialog that pops up in this case. With non-nil 3890file selection dialog that pops up in this case. With non-nil
3891option `todos-filter-done-items', the matches can include not 3891option `todo-filter-done-items', the matches can include not
3892only todo items but also done items, including those in Archive 3892only todo items but also done items, including those in Archive
3893files. 3893files.
3894 3894
3895Called with no prefix ARG, if a regexp items file for the current 3895Called with no prefix ARG, if a regexp items file for the current
3896Todos file has previously been saved (see 3896Todo file has previously been saved (see
3897`todos-save-filtered-items-buffer'), visit this file; if there is 3897`todo-save-filtered-items-buffer'), visit this file; if there is
3898no such file, build the list of regexp items. Called with a 3898no such file, build the list of regexp items. Called with a
3899prefix argument, build the list even if there is a saved file of 3899prefix argument, build the list even if there is a saved file of
3900regexp items." 3900regexp items."
3901 (interactive "P") 3901 (interactive "P")
3902 (todos-filter-items 'regexp arg t)) 3902 (todo-filter-items 'regexp arg t))
3903 3903
3904(defun todos-find-filtered-items-file () 3904(defun todo-find-filtered-items-file ()
3905 "Choose a filtered items file and visit it." 3905 "Choose a filtered items file and visit it."
3906 (interactive) 3906 (interactive)
3907 (let ((files (directory-files todos-directory t "\.tod[rty]$" t)) 3907 (let ((files (directory-files todo-directory t "\.tod[rty]$" t))
3908 falist file) 3908 falist file)
3909 (dolist (f files) 3909 (dolist (f files)
3910 (let ((type (cond ((equal (file-name-extension f) "todr") "regexp") 3910 (let ((type (cond ((equal (file-name-extension f) "todr") "regexp")
3911 ((equal (file-name-extension f) "todt") "top") 3911 ((equal (file-name-extension f) "todt") "top")
3912 ((equal (file-name-extension f) "tody") "diary")))) 3912 ((equal (file-name-extension f) "tody") "diary"))))
3913 (push (cons (concat (todos-short-file-name f) " (" type ")") f) 3913 (push (cons (concat (todo-short-file-name f) " (" type ")") f)
3914 falist))) 3914 falist)))
3915 (setq file (completing-read "Choose a filtered items file: " 3915 (setq file (completing-read "Choose a filtered items file: "
3916 falist nil t nil nil (car falist))) 3916 falist nil t nil nil (car falist)))
3917 (setq file (cdr (assoc-string file falist))) 3917 (setq file (cdr (assoc-string file falist)))
3918 (find-file file))) 3918 (find-file file)))
3919 3919
3920(defun todos-go-to-source-item () 3920(defun todo-go-to-source-item ()
3921 "Display the file and category of the filtered item at point." 3921 "Display the file and category of the filtered item at point."
3922 (interactive) 3922 (interactive)
3923 (let* ((str (todos-item-string)) 3923 (let* ((str (todo-item-string))
3924 (buf (current-buffer)) 3924 (buf (current-buffer))
3925 (res (todos-find-item str)) 3925 (res (todo-find-item str))
3926 (found (nth 0 res)) 3926 (found (nth 0 res))
3927 (file (nth 1 res)) 3927 (file (nth 1 res))
3928 (cat (nth 2 res))) 3928 (cat (nth 2 res)))
@@ -3931,53 +3931,53 @@ regexp items."
3931 (kill-buffer buf) 3931 (kill-buffer buf)
3932 (set-window-buffer (selected-window) 3932 (set-window-buffer (selected-window)
3933 (set-buffer (find-buffer-visiting file))) 3933 (set-buffer (find-buffer-visiting file)))
3934 (setq todos-current-todos-file file) 3934 (setq todo-current-todo-file file)
3935 (setq todos-category-number (todos-category-number cat)) 3935 (setq todo-category-number (todo-category-number cat))
3936 (let ((todos-show-with-done (if (or todos-filter-done-items 3936 (let ((todo-show-with-done (if (or todo-filter-done-items
3937 (eq (cdr found) 'done)) 3937 (eq (cdr found) 'done))
3938 t 3938 t
3939 todos-show-with-done))) 3939 todo-show-with-done)))
3940 (todos-category-select)) 3940 (todo-category-select))
3941 (goto-char (car found))))) 3941 (goto-char (car found)))))
3942 3942
3943(defvar todos-multiple-filter-files nil 3943(defvar todo-multiple-filter-files nil
3944 "List of files selected from `todos-multiple-filter-files' widget.") 3944 "List of files selected from `todo-multiple-filter-files' widget.")
3945 3945
3946(defvar todos-multiple-filter-files-widget nil 3946(defvar todo-multiple-filter-files-widget nil
3947 "Variable holding widget created by `todos-multiple-filter-files'.") 3947 "Variable holding widget created by `todo-multiple-filter-files'.")
3948 3948
3949(defun todos-multiple-filter-files () 3949(defun todo-multiple-filter-files ()
3950 "Pop to a buffer with a widget for choosing multiple filter files." 3950 "Pop to a buffer with a widget for choosing multiple filter files."
3951 (require 'widget) 3951 (require 'widget)
3952 (eval-when-compile 3952 (eval-when-compile
3953 (require 'wid-edit)) 3953 (require 'wid-edit))
3954 (with-current-buffer (get-buffer-create "*Todos Filter Files*") 3954 (with-current-buffer (get-buffer-create "*Todo Filter Files*")
3955 (pop-to-buffer (current-buffer)) 3955 (pop-to-buffer (current-buffer))
3956 (erase-buffer) 3956 (erase-buffer)
3957 (kill-all-local-variables) 3957 (kill-all-local-variables)
3958 (widget-insert "Select files for generating the top priorities list.\n\n") 3958 (widget-insert "Select files for generating the top priorities list.\n\n")
3959 (setq todos-multiple-filter-files-widget 3959 (setq todo-multiple-filter-files-widget
3960 (widget-create 3960 (widget-create
3961 `(set ,@(mapcar (lambda (x) (list 'const x)) 3961 `(set ,@(mapcar (lambda (x) (list 'const x))
3962 (mapcar 'todos-short-file-name 3962 (mapcar 'todo-short-file-name
3963 (funcall todos-files-function)))))) 3963 (funcall todo-files-function))))))
3964 (widget-insert "\n") 3964 (widget-insert "\n")
3965 (widget-create 'push-button 3965 (widget-create 'push-button
3966 :notify (lambda (widget &rest ignore) 3966 :notify (lambda (widget &rest ignore)
3967 (setq todos-multiple-filter-files 'quit) 3967 (setq todo-multiple-filter-files 'quit)
3968 (quit-window t) 3968 (quit-window t)
3969 (exit-recursive-edit)) 3969 (exit-recursive-edit))
3970 "Cancel") 3970 "Cancel")
3971 (widget-insert " ") 3971 (widget-insert " ")
3972 (widget-create 'push-button 3972 (widget-create 'push-button
3973 :notify (lambda (&rest ignore) 3973 :notify (lambda (&rest ignore)
3974 (setq todos-multiple-filter-files 3974 (setq todo-multiple-filter-files
3975 (mapcar (lambda (f) 3975 (mapcar (lambda (f)
3976 (file-truename 3976 (file-truename
3977 (concat todos-directory 3977 (concat todo-directory
3978 f ".todo"))) 3978 f ".todo")))
3979 (widget-value 3979 (widget-value
3980 todos-multiple-filter-files-widget))) 3980 todo-multiple-filter-files-widget)))
3981 (quit-window t) 3981 (quit-window t)
3982 (exit-recursive-edit)) 3982 (exit-recursive-edit))
3983 "Apply") 3983 "Apply")
@@ -3986,19 +3986,19 @@ regexp items."
3986 (message "Click \"Apply\" after selecting files.") 3986 (message "Click \"Apply\" after selecting files.")
3987 (recursive-edit)) 3987 (recursive-edit))
3988 3988
3989(defconst todos-filtered-items-buffer "Todos filtered items" 3989(defconst todo-filtered-items-buffer "Todo filtered items"
3990 "Initial name of buffer in Todos Filter Items mode.") 3990 "Initial name of buffer in Todo Filter Items mode.")
3991 3991
3992(defconst todos-top-priorities-buffer "Todos top priorities" 3992(defconst todo-top-priorities-buffer "Todo top priorities"
3993 "Buffer type string for `todos-filter-items'.") 3993 "Buffer type string for `todo-filter-items'.")
3994 3994
3995(defconst todos-diary-items-buffer "Todos diary items" 3995(defconst todo-diary-items-buffer "Todo diary items"
3996 "Buffer type string for `todos-filter-items'.") 3996 "Buffer type string for `todo-filter-items'.")
3997 3997
3998(defconst todos-regexp-items-buffer "Todos regexp items" 3998(defconst todo-regexp-items-buffer "Todo regexp items"
3999 "Buffer type string for `todos-filter-items'.") 3999 "Buffer type string for `todo-filter-items'.")
4000 4000
4001(defun todos-filter-items (filter &optional new multifile) 4001(defun todo-filter-items (filter &optional new multifile)
4002 "Display a cross-categorial list of items filtered by FILTER. 4002 "Display a cross-categorial list of items filtered by FILTER.
4003The values of FILTER can be `top' for top priority items, a cons 4003The values of FILTER can be `top' for top priority items, a cons
4004of `top' and a number passed by the caller, `diary' for diary 4004of `top' and a number passed by the caller, `diary' for diary
@@ -4010,56 +4010,56 @@ items; if there is no such file, or with non-nil NEW, build the
4010list and display it. 4010list and display it.
4011 4011
4012See the document strings of the commands 4012See the document strings of the commands
4013`todos-filter-top-priorities', `todos-filter-diary-items', 4013`todo-filter-top-priorities', `todo-filter-diary-items',
4014`todos-filter-regexp-items', and those of the corresponding 4014`todo-filter-regexp-items', and those of the corresponding
4015multifile commands for further details." 4015multifile commands for further details."
4016 (let* ((top (eq filter 'top)) 4016 (let* ((top (eq filter 'top))
4017 (diary (eq filter 'diary)) 4017 (diary (eq filter 'diary))
4018 (regexp (eq filter 'regexp)) 4018 (regexp (eq filter 'regexp))
4019 (buf (cond (top todos-top-priorities-buffer) 4019 (buf (cond (top todo-top-priorities-buffer)
4020 (diary todos-diary-items-buffer) 4020 (diary todo-diary-items-buffer)
4021 (regexp todos-regexp-items-buffer))) 4021 (regexp todo-regexp-items-buffer)))
4022 (flist (if multifile 4022 (flist (if multifile
4023 (or todos-filter-files 4023 (or todo-filter-files
4024 (progn (todos-multiple-filter-files) 4024 (progn (todo-multiple-filter-files)
4025 todos-multiple-filter-files)) 4025 todo-multiple-filter-files))
4026 (list todos-current-todos-file))) 4026 (list todo-current-todo-file)))
4027 (multi (> (length flist) 1)) 4027 (multi (> (length flist) 1))
4028 (fname (if (equal flist 'quit) 4028 (fname (if (equal flist 'quit)
4029 ;; Pressed `cancel' in t-m-f-f file selection dialog. 4029 ;; Pressed `cancel' in t-m-f-f file selection dialog.
4030 (keyboard-quit) 4030 (keyboard-quit)
4031 (concat todos-directory 4031 (concat todo-directory
4032 (mapconcat 'todos-short-file-name flist "-") 4032 (mapconcat 'todo-short-file-name flist "-")
4033 (cond (top ".todt") 4033 (cond (top ".todt")
4034 (diary ".tody") 4034 (diary ".tody")
4035 (regexp ".todr"))))) 4035 (regexp ".todr")))))
4036 (rxfiles (when regexp 4036 (rxfiles (when regexp
4037 (directory-files todos-directory t ".*\\.todr$" t))) 4037 (directory-files todo-directory t ".*\\.todr$" t)))
4038 (file-exists (or (file-exists-p fname) rxfiles))) 4038 (file-exists (or (file-exists-p fname) rxfiles)))
4039 (cond ((and top new (natnump new)) 4039 (cond ((and top new (natnump new))
4040 (todos-filter-items-1 (cons 'top new) flist)) 4040 (todo-filter-items-1 (cons 'top new) flist))
4041 ((and (not new) file-exists) 4041 ((and (not new) file-exists)
4042 (when (and rxfiles (> (length rxfiles) 1)) 4042 (when (and rxfiles (> (length rxfiles) 1))
4043 (let ((rxf (mapcar 'todos-short-file-name rxfiles))) 4043 (let ((rxf (mapcar 'todo-short-file-name rxfiles)))
4044 (setq fname (todos-absolute-file-name 4044 (setq fname (todo-absolute-file-name
4045 (completing-read "Choose a regexp items file: " 4045 (completing-read "Choose a regexp items file: "
4046 rxf) 'regexp)))) 4046 rxf) 'regexp))))
4047 (find-file fname) 4047 (find-file fname)
4048 (todos-prefix-overlays) 4048 (todo-prefix-overlays)
4049 (todos-check-filtered-items-file)) 4049 (todo-check-filtered-items-file))
4050 (t 4050 (t
4051 (todos-filter-items-1 filter flist))) 4051 (todo-filter-items-1 filter flist)))
4052 (setq fname (replace-regexp-in-string "-" ", " 4052 (setq fname (replace-regexp-in-string "-" ", "
4053 (todos-short-file-name fname))) 4053 (todo-short-file-name fname)))
4054 (rename-buffer (format (concat "%s for file" (if multi "s" "") 4054 (rename-buffer (format (concat "%s for file" (if multi "s" "")
4055 " \"%s\"") buf fname)))) 4055 " \"%s\"") buf fname))))
4056 4056
4057(defun todos-filter-items-1 (filter file-list) 4057(defun todo-filter-items-1 (filter file-list)
4058 "Build a list of items by applying FILTER to FILE-LIST. 4058 "Build a list of items by applying FILTER to FILE-LIST.
4059Internal subroutine called by `todos-filter-items', which passes 4059Internal subroutine called by `todo-filter-items', which passes
4060the values of FILTER and FILE-LIST." 4060the values of FILTER and FILE-LIST."
4061 (let ((num (if (consp filter) (cdr filter) todos-top-priorities)) 4061 (let ((num (if (consp filter) (cdr filter) todo-top-priorities))
4062 (buf (get-buffer-create todos-filtered-items-buffer)) 4062 (buf (get-buffer-create todo-filtered-items-buffer))
4063 (multifile (> (length file-list) 1)) 4063 (multifile (> (length file-list) 1))
4064 regexp fname bufstr cat beg end done) 4064 regexp fname bufstr cat beg end done)
4065 (if (null file-list) 4065 (if (null file-list)
@@ -4067,7 +4067,7 @@ the values of FILTER and FILE-LIST."
4067 (with-current-buffer buf 4067 (with-current-buffer buf
4068 (erase-buffer) 4068 (erase-buffer)
4069 (kill-all-local-variables) 4069 (kill-all-local-variables)
4070 (todos-filtered-items-mode)) 4070 (todo-filtered-items-mode))
4071 (when (eq filter 'regexp) 4071 (when (eq filter 'regexp)
4072 (setq regexp (read-string "Enter a regular expression: "))) 4072 (setq regexp (read-string "Enter a regular expression: ")))
4073 (save-current-buffer 4073 (save-current-buffer
@@ -4077,74 +4077,74 @@ the values of FILTER and FILE-LIST."
4077 (let ((bf (find-buffer-visiting f))) 4077 (let ((bf (find-buffer-visiting f)))
4078 (when (buffer-modified-p bf) 4078 (when (buffer-modified-p bf)
4079 (with-current-buffer bf (save-buffer)))) 4079 (with-current-buffer bf (save-buffer))))
4080 (setq fname (todos-short-file-name f)) 4080 (setq fname (todo-short-file-name f))
4081 (with-temp-buffer 4081 (with-temp-buffer
4082 (when (and todos-filter-done-items (eq filter 'regexp)) 4082 (when (and todo-filter-done-items (eq filter 'regexp))
4083 ;; If there is a corresponding archive file for the 4083 ;; If there is a corresponding archive file for the
4084 ;; Todos file, insert it first and add identifiers for 4084 ;; Todo file, insert it first and add identifiers for
4085 ;; todos-go-to-source-item. 4085 ;; todo-go-to-source-item.
4086 (let ((arch (concat (file-name-sans-extension f) ".toda"))) 4086 (let ((arch (concat (file-name-sans-extension f) ".toda")))
4087 (when (file-exists-p arch) 4087 (when (file-exists-p arch)
4088 (insert-file-contents arch) 4088 (insert-file-contents arch)
4089 ;; Delete Todos archive file categories sexp. 4089 ;; Delete Todo archive file categories sexp.
4090 (delete-region (line-beginning-position) 4090 (delete-region (line-beginning-position)
4091 (1+ (line-end-position))) 4091 (1+ (line-end-position)))
4092 (save-excursion 4092 (save-excursion
4093 (while (not (eobp)) 4093 (while (not (eobp))
4094 (when (re-search-forward 4094 (when (re-search-forward
4095 (concat (if todos-filter-done-items 4095 (concat (if todo-filter-done-items
4096 (concat "\\(?:" todos-done-string-start 4096 (concat "\\(?:" todo-done-string-start
4097 "\\|" todos-date-string-start 4097 "\\|" todo-date-string-start
4098 "\\)") 4098 "\\)")
4099 todos-date-string-start) 4099 todo-date-string-start)
4100 todos-date-pattern "\\(?: " 4100 todo-date-pattern "\\(?: "
4101 diary-time-regexp "\\)?" 4101 diary-time-regexp "\\)?"
4102 (if todos-filter-done-items 4102 (if todo-filter-done-items
4103 "\\]" 4103 "\\]"
4104 (regexp-quote todos-nondiary-end)) "?") 4104 (regexp-quote todo-nondiary-end)) "?")
4105 nil t) 4105 nil t)
4106 (insert "(archive) ")) 4106 (insert "(archive) "))
4107 (forward-line)))))) 4107 (forward-line))))))
4108 (insert-file-contents f) 4108 (insert-file-contents f)
4109 ;; Delete Todos file categories sexp. 4109 ;; Delete Todo file categories sexp.
4110 (delete-region (line-beginning-position) (1+ (line-end-position))) 4110 (delete-region (line-beginning-position) (1+ (line-end-position)))
4111 (let (fnum) 4111 (let (fnum)
4112 ;; Unless the number of top priorities to show was 4112 ;; Unless the number of top priorities to show was
4113 ;; passed by the caller, the file-wide value from 4113 ;; passed by the caller, the file-wide value from
4114 ;; `todos-top-priorities-overrides', if non-nil, overrides 4114 ;; `todo-top-priorities-overrides', if non-nil, overrides
4115 ;; `todos-top-priorities'. 4115 ;; `todo-top-priorities'.
4116 (unless (consp filter) 4116 (unless (consp filter)
4117 (setq fnum (or (nth 1 (assoc f todos-top-priorities-overrides)) 4117 (setq fnum (or (nth 1 (assoc f todo-top-priorities-overrides))
4118 todos-top-priorities))) 4118 todo-top-priorities)))
4119 (while (re-search-forward 4119 (while (re-search-forward
4120 (concat "^" (regexp-quote todos-category-beg) 4120 (concat "^" (regexp-quote todo-category-beg)
4121 "\\(.+\\)\n") nil t) 4121 "\\(.+\\)\n") nil t)
4122 (setq cat (match-string 1)) 4122 (setq cat (match-string 1))
4123 (let (cnum) 4123 (let (cnum)
4124 ;; Unless the number of top priorities to show was 4124 ;; Unless the number of top priorities to show was
4125 ;; passed by the caller, the category-wide value 4125 ;; passed by the caller, the category-wide value
4126 ;; from `todos-top-priorities-overrides', if non-nil, 4126 ;; from `todo-top-priorities-overrides', if non-nil,
4127 ;; overrides a non-nil file-wide value from 4127 ;; overrides a non-nil file-wide value from
4128 ;; `todos-top-priorities-overrides' as well as 4128 ;; `todo-top-priorities-overrides' as well as
4129 ;; `todos-top-priorities'. 4129 ;; `todo-top-priorities'.
4130 (unless (consp filter) 4130 (unless (consp filter)
4131 (let ((cats (nth 2 (assoc f todos-top-priorities-overrides)))) 4131 (let ((cats (nth 2 (assoc f todo-top-priorities-overrides))))
4132 (setq cnum (or (cdr (assoc cat cats)) fnum)))) 4132 (setq cnum (or (cdr (assoc cat cats)) fnum))))
4133 (delete-region (match-beginning 0) (match-end 0)) 4133 (delete-region (match-beginning 0) (match-end 0))
4134 (setq beg (point)) ; First item in the current category. 4134 (setq beg (point)) ; First item in the current category.
4135 (setq end (if (re-search-forward 4135 (setq end (if (re-search-forward
4136 (concat "^" (regexp-quote todos-category-beg)) 4136 (concat "^" (regexp-quote todo-category-beg))
4137 nil t) 4137 nil t)
4138 (match-beginning 0) 4138 (match-beginning 0)
4139 (point-max))) 4139 (point-max)))
4140 (goto-char beg) 4140 (goto-char beg)
4141 (setq done 4141 (setq done
4142 (if (re-search-forward 4142 (if (re-search-forward
4143 (concat "\n" (regexp-quote todos-category-done)) 4143 (concat "\n" (regexp-quote todo-category-done))
4144 end t) 4144 end t)
4145 (match-beginning 0) 4145 (match-beginning 0)
4146 end)) 4146 end))
4147 (unless (and todos-filter-done-items (eq filter 'regexp)) 4147 (unless (and todo-filter-done-items (eq filter 'regexp))
4148 ;; Leave done items. 4148 ;; Leave done items.
4149 (delete-region done end) 4149 (delete-region done end)
4150 (setq end done)) 4150 (setq end done))
@@ -4153,26 +4153,26 @@ the values of FILTER and FILE-LIST."
4153 ;; Apply the filter. 4153 ;; Apply the filter.
4154 (cond ((eq filter 'diary) 4154 (cond ((eq filter 'diary)
4155 (while (not (eobp)) 4155 (while (not (eobp))
4156 (if (looking-at (regexp-quote todos-nondiary-start)) 4156 (if (looking-at (regexp-quote todo-nondiary-start))
4157 (todos-remove-item) 4157 (todo-remove-item)
4158 (todos-forward-item)))) 4158 (todo-forward-item))))
4159 ((eq filter 'regexp) 4159 ((eq filter 'regexp)
4160 (while (not (eobp)) 4160 (while (not (eobp))
4161 (if (looking-at todos-item-start) 4161 (if (looking-at todo-item-start)
4162 (if (string-match regexp (todos-item-string)) 4162 (if (string-match regexp (todo-item-string))
4163 (todos-forward-item) 4163 (todo-forward-item)
4164 (todos-remove-item)) 4164 (todo-remove-item))
4165 ;; Kill lines that aren't part of a todo or done 4165 ;; Kill lines that aren't part of a todo or done
4166 ;; item (empty or todos-category-done). 4166 ;; item (empty or todo-category-done).
4167 (delete-region (line-beginning-position) 4167 (delete-region (line-beginning-position)
4168 (1+ (line-end-position)))) 4168 (1+ (line-end-position))))
4169 ;; If last todo item in file matches regexp and 4169 ;; If last todo item in file matches regexp and
4170 ;; there are no following done items, 4170 ;; there are no following done items,
4171 ;; todos-category-done string is left dangling, 4171 ;; todo-category-done string is left dangling,
4172 ;; because todos-forward-item jumps over it. 4172 ;; because todo-forward-item jumps over it.
4173 (if (and (eobp) 4173 (if (and (eobp)
4174 (looking-back 4174 (looking-back
4175 (concat (regexp-quote todos-done-string) 4175 (concat (regexp-quote todo-done-string)
4176 "\n"))) 4176 "\n")))
4177 (delete-region (point) (progn 4177 (delete-region (point) (progn
4178 (forward-line -2) 4178 (forward-line -2)
@@ -4180,7 +4180,7 @@ the values of FILTER and FILE-LIST."
4180 (t ; Filter top priority items. 4180 (t ; Filter top priority items.
4181 (setq num (or cnum fnum num)) 4181 (setq num (or cnum fnum num))
4182 (unless (zerop num) 4182 (unless (zerop num)
4183 (todos-forward-item num)))) 4183 (todo-forward-item num))))
4184 (setq beg (point)) 4184 (setq beg (point))
4185 ;; Delete non-top-priority items. 4185 ;; Delete non-top-priority items.
4186 (unless (member filter '(diary regexp)) 4186 (unless (member filter '(diary regexp))
@@ -4190,15 +4190,15 @@ the values of FILTER and FILE-LIST."
4190 ;; item. 4190 ;; item.
4191 (while (not (eobp)) 4191 (while (not (eobp))
4192 (when (re-search-forward 4192 (when (re-search-forward
4193 (concat (if todos-filter-done-items 4193 (concat (if todo-filter-done-items
4194 (concat "\\(?:" todos-done-string-start 4194 (concat "\\(?:" todo-done-string-start
4195 "\\|" todos-date-string-start 4195 "\\|" todo-date-string-start
4196 "\\)") 4196 "\\)")
4197 todos-date-string-start) 4197 todo-date-string-start)
4198 todos-date-pattern "\\(?: " diary-time-regexp 4198 todo-date-pattern "\\(?: " diary-time-regexp
4199 "\\)?" (if todos-filter-done-items 4199 "\\)?" (if todo-filter-done-items
4200 "\\]" 4200 "\\]"
4201 (regexp-quote todos-nondiary-end)) 4201 (regexp-quote todo-nondiary-end))
4202 "?") 4202 "?")
4203 nil t) 4203 nil t)
4204 (insert " [") 4204 (insert " [")
@@ -4211,27 +4211,27 @@ the values of FILTER and FILE-LIST."
4211 (let (buffer-read-only) 4211 (let (buffer-read-only)
4212 (insert bufstr))))))) 4212 (insert bufstr)))))))
4213 (set-window-buffer (selected-window) (set-buffer buf)) 4213 (set-window-buffer (selected-window) (set-buffer buf))
4214 (todos-prefix-overlays) 4214 (todo-prefix-overlays)
4215 (goto-char (point-min))))) 4215 (goto-char (point-min)))))
4216 4216
4217(defun todos-set-top-priorities (&optional arg) 4217(defun todo-set-top-priorities (&optional arg)
4218 "Set number of top priorities shown by `todos-filter-top-priorities'. 4218 "Set number of top priorities shown by `todo-filter-top-priorities'.
4219With non-nil ARG, set the number only for the current Todos 4219With non-nil ARG, set the number only for the current Todo
4220category; otherwise, set the number for all categories in the 4220category; otherwise, set the number for all categories in the
4221current Todos file. 4221current Todo file.
4222 4222
4223Calling this function via either of the commands 4223Calling this function via either of the commands
4224`todos-set-top-priorities-in-file' or 4224`todo-set-top-priorities-in-file' or
4225`todos-set-top-priorities-in-category' is the recommended way to 4225`todo-set-top-priorities-in-category' is the recommended way to
4226set the user customizable option `todos-top-priorities-overrides'." 4226set the user customizable option `todo-top-priorities-overrides'."
4227 (let* ((cat (todos-current-category)) 4227 (let* ((cat (todo-current-category))
4228 (file todos-current-todos-file) 4228 (file todo-current-todo-file)
4229 (rules todos-top-priorities-overrides) 4229 (rules todo-top-priorities-overrides)
4230 (frule (assoc-string file rules)) 4230 (frule (assoc-string file rules))
4231 (crule (assoc-string cat (nth 2 frule))) 4231 (crule (assoc-string cat (nth 2 frule)))
4232 (crules (nth 2 frule)) 4232 (crules (nth 2 frule))
4233 (cur (or (if arg (cdr crule) (nth 1 frule)) 4233 (cur (or (if arg (cdr crule) (nth 1 frule))
4234 todos-top-priorities)) 4234 todo-top-priorities))
4235 (prompt (if arg (concat "Number of top priorities in this category" 4235 (prompt (if arg (concat "Number of top priorities in this category"
4236 " (currently %d): ") 4236 " (currently %d): ")
4237 (concat "Default number of top priorities per category" 4237 (concat "Default number of top priorities per category"
@@ -4250,25 +4250,25 @@ set the user customizable option `todos-top-priorities-overrides'."
4250 (list file cur nrule) 4250 (list file cur nrule)
4251 nrule) 4251 nrule)
4252 (delete frule rules))) 4252 (delete frule rules)))
4253 (customize-save-variable 'todos-top-priorities-overrides rules) 4253 (customize-save-variable 'todo-top-priorities-overrides rules)
4254 (todos-prefix-overlays))) 4254 (todo-prefix-overlays)))
4255 4255
4256(defun todos-find-item (str) 4256(defun todo-find-item (str)
4257 "Search for filtered item STR in its saved Todos file. 4257 "Search for filtered item STR in its saved Todo file.
4258Return the list (FOUND FILE CAT), where CAT and FILE are the 4258Return the list (FOUND FILE CAT), where CAT and FILE are the
4259item's category and file, and FOUND is a cons cell if the search 4259item's category and file, and FOUND is a cons cell if the search
4260succeeds, whose car is the start of the item in FILE and whose 4260succeeds, whose car is the start of the item in FILE and whose
4261cdr is `done', if the item is now a done item, `changed', if its 4261cdr is `done', if the item is now a done item, `changed', if its
4262text was truncated or augmented or, for a top priority item, if 4262text was truncated or augmented or, for a top priority item, if
4263its priority has changed, and `same' otherwise." 4263its priority has changed, and `same' otherwise."
4264 (string-match (concat (if todos-filter-done-items 4264 (string-match (concat (if todo-filter-done-items
4265 (concat "\\(?:" todos-done-string-start "\\|" 4265 (concat "\\(?:" todo-done-string-start "\\|"
4266 todos-date-string-start "\\)") 4266 todo-date-string-start "\\)")
4267 todos-date-string-start) 4267 todo-date-string-start)
4268 todos-date-pattern "\\(?: " diary-time-regexp "\\)?" 4268 todo-date-pattern "\\(?: " diary-time-regexp "\\)?"
4269 (if todos-filter-done-items 4269 (if todo-filter-done-items
4270 "\\]" 4270 "\\]"
4271 (regexp-quote todos-nondiary-end)) "?" 4271 (regexp-quote todo-nondiary-end)) "?"
4272 "\\(?4: \\[\\(?3:(archive) \\)?\\(?2:.*:\\)?" 4272 "\\(?4: \\[\\(?3:(archive) \\)?\\(?2:.*:\\)?"
4273 "\\(?1:.*\\)\\]\\).*$") str) 4273 "\\(?1:.*\\)\\]\\).*$") str)
4274 (let ((cat (match-string 1 str)) 4274 (let ((cat (match-string 1 str))
@@ -4285,27 +4285,27 @@ its priority has changed, and `same' otherwise."
4285 (while (search-backward filcat nil t) 4285 (while (search-backward filcat nil t)
4286 (setq tpriority (1+ tpriority))))) 4286 (setq tpriority (1+ tpriority)))))
4287 (setq file (if file 4287 (setq file (if file
4288 (concat todos-directory (substring file 0 -1) 4288 (concat todo-directory (substring file 0 -1)
4289 (if archive ".toda" ".todo")) 4289 (if archive ".toda" ".todo"))
4290 (if archive 4290 (if archive
4291 (concat (file-name-sans-extension 4291 (concat (file-name-sans-extension
4292 todos-global-current-todos-file) ".toda") 4292 todo-global-current-todo-file) ".toda")
4293 todos-global-current-todos-file))) 4293 todo-global-current-todo-file)))
4294 (find-file-noselect file) 4294 (find-file-noselect file)
4295 (with-current-buffer (find-buffer-visiting file) 4295 (with-current-buffer (find-buffer-visiting file)
4296 (save-restriction 4296 (save-restriction
4297 (widen) 4297 (widen)
4298 (goto-char (point-min)) 4298 (goto-char (point-min))
4299 (let ((beg (re-search-forward 4299 (let ((beg (re-search-forward
4300 (concat "^" (regexp-quote (concat todos-category-beg cat)) 4300 (concat "^" (regexp-quote (concat todo-category-beg cat))
4301 "$") 4301 "$")
4302 nil t)) 4302 nil t))
4303 (done (save-excursion 4303 (done (save-excursion
4304 (re-search-forward 4304 (re-search-forward
4305 (concat "^" (regexp-quote todos-category-done)) nil t))) 4305 (concat "^" (regexp-quote todo-category-done)) nil t)))
4306 (end (save-excursion 4306 (end (save-excursion
4307 (or (re-search-forward 4307 (or (re-search-forward
4308 (concat "^" (regexp-quote todos-category-beg)) 4308 (concat "^" (regexp-quote todo-category-beg))
4309 nil t) 4309 nil t)
4310 (point-max))))) 4310 (point-max)))))
4311 (setq found (when (search-forward str end t) 4311 (setq found (when (search-forward str end t)
@@ -4320,33 +4320,33 @@ its priority has changed, and `same' otherwise."
4320 ;; Not top item in category. 4320 ;; Not top item in category.
4321 (while (> (point) (1+ beg)) 4321 (while (> (point) (1+ beg))
4322 (let ((opoint (point))) 4322 (let ((opoint (point)))
4323 (todos-backward-item) 4323 (todo-backward-item)
4324 ;; Can't move backward beyond 4324 ;; Can't move backward beyond
4325 ;; first item in file. 4325 ;; first item in file.
4326 (unless (= (point) opoint) 4326 (unless (= (point) opoint)
4327 (setq cpriority (1+ cpriority))))))) 4327 (setq cpriority (1+ cpriority)))))))
4328 (if (and (= tpriority cpriority) 4328 (if (and (= tpriority cpriority)
4329 ;; Proper substring is not the same. 4329 ;; Proper substring is not the same.
4330 (string= (todos-item-string) 4330 (string= (todo-item-string)
4331 str)) 4331 str))
4332 'same 4332 'same
4333 'changed))))))))) 4333 'changed)))))))))
4334 (list found file cat))) 4334 (list found file cat)))
4335 4335
4336(defun todos-check-filtered-items-file () 4336(defun todo-check-filtered-items-file ()
4337 "Check if filtered items file is up to date and a show suitable message." 4337 "Check if filtered items file is up to date and a show suitable message."
4338 ;; (catch 'old 4338 ;; (catch 'old
4339 (let ((count 0)) 4339 (let ((count 0))
4340 (while (not (eobp)) 4340 (while (not (eobp))
4341 (let* ((item (todos-item-string)) 4341 (let* ((item (todo-item-string))
4342 (found (car (todos-find-item item)))) 4342 (found (car (todo-find-item item))))
4343 (unless (eq (cdr found) 'same) 4343 (unless (eq (cdr found) 'same)
4344 (save-excursion 4344 (save-excursion
4345 (overlay-put (make-overlay (todos-item-start) (todos-item-end)) 4345 (overlay-put (make-overlay (todo-item-start) (todo-item-end))
4346 'face 'todos-search)) 4346 'face 'todo-search))
4347 (setq count (1+ count)))) 4347 (setq count (1+ count))))
4348 ;; (throw 'old (message "The marked item is not up to date."))) 4348 ;; (throw 'old (message "The marked item is not up to date.")))
4349 (todos-forward-item)) 4349 (todo-forward-item))
4350 (if (zerop count) 4350 (if (zerop count)
4351 (message "Filtered items file is up to date.") 4351 (message "Filtered items file is up to date.")
4352 (message (concat "The highlighted item" (if (= count 1) " is " "s are ") 4352 (message (concat "The highlighted item" (if (= count 1) " is " "s are ")
@@ -4354,7 +4354,7 @@ its priority has changed, and `same' otherwise."
4354 ;; "\nType <return> on item for details." 4354 ;; "\nType <return> on item for details."
4355 ))))) 4355 )))))
4356 4356
4357(defun todos-filter-items-filename () 4357(defun todo-filter-items-filename ()
4358 "Return absolute file name for saving this Filtered Items buffer." 4358 "Return absolute file name for saving this Filtered Items buffer."
4359 (let ((bufname (buffer-name))) 4359 (let ((bufname (buffer-name)))
4360 (string-match "\"\\([^\"]+\\)\"" bufname) 4360 (string-match "\"\\([^\"]+\\)\"" bufname)
@@ -4367,48 +4367,48 @@ its priority has changed, and `same' otherwise."
4367 (let ((prompt (concat "Enter a short identifying string" 4367 (let ((prompt (concat "Enter a short identifying string"
4368 " to make this file name unique: "))) 4368 " to make this file name unique: ")))
4369 (setq filename-base (concat filename-base "-" (read-string prompt))))) 4369 (setq filename-base (concat filename-base "-" (read-string prompt)))))
4370 (concat todos-directory filename-base 4370 (concat todo-directory filename-base
4371 (cond (top-priorities ".todt") 4371 (cond (top-priorities ".todt")
4372 (diary-items ".tody") 4372 (diary-items ".tody")
4373 (regexp-items ".todr")))))) 4373 (regexp-items ".todr"))))))
4374 4374
4375(defun todos-save-filtered-items-buffer () 4375(defun todo-save-filtered-items-buffer ()
4376 "Save current Filtered Items buffer to a file. 4376 "Save current Filtered Items buffer to a file.
4377If the file already exists, overwrite it only on confirmation." 4377If the file already exists, overwrite it only on confirmation."
4378 (let ((filename (or (buffer-file-name) (todos-filter-items-filename)))) 4378 (let ((filename (or (buffer-file-name) (todo-filter-items-filename))))
4379 (write-file filename t))) 4379 (write-file filename t)))
4380 4380
4381;; ----------------------------------------------------------------------------- 4381;; -----------------------------------------------------------------------------
4382;;; Printing Todos buffers 4382;;; Printing Todo buffers
4383;; ----------------------------------------------------------------------------- 4383;; -----------------------------------------------------------------------------
4384 4384
4385(defcustom todos-print-buffer-function 'ps-print-buffer-with-faces 4385(defcustom todo-print-buffer-function 'ps-print-buffer-with-faces
4386 "Function called by the command `todos-print-buffer'." 4386 "Function called by the command `todo-print-buffer'."
4387 :type 'symbol 4387 :type 'symbol
4388 :group 'todos) 4388 :group 'todo)
4389 4389
4390(defvar todos-print-buffer "*Todos Print*" 4390(defvar todo-print-buffer "*Todo Print*"
4391 "Name of buffer containing printable Todos text.") 4391 "Name of buffer containing printable Todo text.")
4392 4392
4393(defun todos-print-buffer (&optional to-file) 4393(defun todo-print-buffer (&optional to-file)
4394 "Produce a printable version of the current Todos buffer. 4394 "Produce a printable version of the current Todo buffer.
4395This converts overlays and soft line wrapping and, depending on 4395This converts overlays and soft line wrapping and, depending on
4396the value of `todos-print-buffer-function', includes faces. With 4396the value of `todo-print-buffer-function', includes faces. With
4397non-nil argument TO-FILE write the printable version to a file; 4397non-nil argument TO-FILE write the printable version to a file;
4398otherwise, send it to the default printer." 4398otherwise, send it to the default printer."
4399 (interactive) 4399 (interactive)
4400 (let ((buf todos-print-buffer) 4400 (let ((buf todo-print-buffer)
4401 (header (cond 4401 (header (cond
4402 ((eq major-mode 'todos-mode) 4402 ((eq major-mode 'todo-mode)
4403 (concat "Todos File: " 4403 (concat "Todo File: "
4404 (todos-short-file-name todos-current-todos-file) 4404 (todo-short-file-name todo-current-todo-file)
4405 "\nCategory: " (todos-current-category))) 4405 "\nCategory: " (todo-current-category)))
4406 ((eq major-mode 'todos-filtered-items-mode) 4406 ((eq major-mode 'todo-filtered-items-mode)
4407 (buffer-name)))) 4407 (buffer-name))))
4408 (prefix (propertize (concat todos-prefix " ") 4408 (prefix (propertize (concat todo-prefix " ")
4409 'face 'todos-prefix-string)) 4409 'face 'todo-prefix-string))
4410 (num 0) 4410 (num 0)
4411 (fill-prefix (make-string todos-indent-to-here 32)) 4411 (fill-prefix (make-string todo-indent-to-here 32))
4412 (content (buffer-string)) 4412 (content (buffer-string))
4413 file) 4413 file)
4414 (with-current-buffer (get-buffer-create buf) 4414 (with-current-buffer (get-buffer-create buf)
@@ -4416,45 +4416,45 @@ otherwise, send it to the default printer."
4416 (goto-char (point-min)) 4416 (goto-char (point-min))
4417 (while (not (eobp)) 4417 (while (not (eobp))
4418 (let ((beg (point)) 4418 (let ((beg (point))
4419 (end (save-excursion (todos-item-end)))) 4419 (end (save-excursion (todo-item-end))))
4420 (when todos-number-prefix 4420 (when todo-number-prefix
4421 (setq num (1+ num)) 4421 (setq num (1+ num))
4422 (setq prefix (propertize (concat (number-to-string num) " ") 4422 (setq prefix (propertize (concat (number-to-string num) " ")
4423 'face 'todos-prefix-string))) 4423 'face 'todo-prefix-string)))
4424 (insert prefix) 4424 (insert prefix)
4425 (fill-region beg end)) 4425 (fill-region beg end))
4426 ;; Calling todos-forward-item infloops at todos-item-start due to 4426 ;; Calling todo-forward-item infloops at todo-item-start due to
4427 ;; non-overlay prefix, so search for item start instead. 4427 ;; non-overlay prefix, so search for item start instead.
4428 (if (re-search-forward todos-item-start nil t) 4428 (if (re-search-forward todo-item-start nil t)
4429 (beginning-of-line) 4429 (beginning-of-line)
4430 (goto-char (point-max)))) 4430 (goto-char (point-max))))
4431 (if (re-search-backward (concat "^" (regexp-quote todos-category-done)) 4431 (if (re-search-backward (concat "^" (regexp-quote todo-category-done))
4432 nil t) 4432 nil t)
4433 (replace-match todos-done-separator)) 4433 (replace-match todo-done-separator))
4434 (goto-char (point-min)) 4434 (goto-char (point-min))
4435 (insert header) 4435 (insert header)
4436 (newline 2) 4436 (newline 2)
4437 (if to-file 4437 (if to-file
4438 (let ((file (read-file-name "Print to file: "))) 4438 (let ((file (read-file-name "Print to file: ")))
4439 (funcall todos-print-buffer-function file)) 4439 (funcall todo-print-buffer-function file))
4440 (funcall todos-print-buffer-function))) 4440 (funcall todo-print-buffer-function)))
4441 (kill-buffer buf))) 4441 (kill-buffer buf)))
4442 4442
4443(defun todos-print-buffer-to-file () 4443(defun todo-print-buffer-to-file ()
4444 "Save printable version of this Todos buffer to a file." 4444 "Save printable version of this Todo buffer to a file."
4445 (interactive) 4445 (interactive)
4446 (todos-print-buffer t)) 4446 (todo-print-buffer t))
4447 4447
4448;; ----------------------------------------------------------------------------- 4448;; -----------------------------------------------------------------------------
4449;;; Legacy Todo mode files 4449;;; Legacy Todo mode files
4450;; ----------------------------------------------------------------------------- 4450;; -----------------------------------------------------------------------------
4451 4451
4452(defcustom todos-legacy-date-time-regexp 4452(defcustom todo-legacy-date-time-regexp
4453 (concat "\\(?1:[0-9]\\{4\\}\\)-\\(?2:[0-9]\\{2\\}\\)-" 4453 (concat "\\(?1:[0-9]\\{4\\}\\)-\\(?2:[0-9]\\{2\\}\\)-"
4454 "\\(?3:[0-9]\\{2\\}\\) \\(?4:[0-9]\\{2\\}:[0-9]\\{2\\}\\)") 4454 "\\(?3:[0-9]\\{2\\}\\) \\(?4:[0-9]\\{2\\}:[0-9]\\{2\\}\\)")
4455 "Regexp matching legacy todo-mode.el item date-time strings. 4455 "Regexp matching legacy todo-mode.el item date-time strings.
4456In order for `todos-convert-legacy-files' to correctly convert this 4456In order for `todo-convert-legacy-files' to correctly convert this
4457string to the current Todos format, the regexp must contain four 4457string to the current Todo format, the regexp must contain four
4458explicitly numbered groups (see `(elisp) Regexp Backslash'), 4458explicitly numbered groups (see `(elisp) Regexp Backslash'),
4459where group 1 matches a string for the year, group 2 a string for 4459where group 1 matches a string for the year, group 2 a string for
4460the month, group 3 a string for the day and group 4 a string for 4460the month, group 3 a string for the day and group 4 a string for
@@ -4462,11 +4462,11 @@ the time. The default value converts date-time strings built
4462using the default value of `todo-time-string-format' from 4462using the default value of `todo-time-string-format' from
4463todo-mode.el." 4463todo-mode.el."
4464 :type 'regexp 4464 :type 'regexp
4465 :group 'todos) 4465 :group 'todo)
4466 4466
4467(defun todos-convert-legacy-date-time () 4467(defun todo-convert-legacy-date-time ()
4468 "Return converted date-time string. 4468 "Return converted date-time string.
4469Helper function for `todos-convert-legacy-files'." 4469Helper function for `todo-convert-legacy-files'."
4470 (let* ((year (match-string 1)) 4470 (let* ((year (match-string 1))
4471 (month (match-string 2)) 4471 (month (match-string 2))
4472 (monthname (calendar-month-name (string-to-number month) t)) 4472 (monthname (calendar-month-name (string-to-number month) t))
@@ -4477,28 +4477,28 @@ Helper function for `todos-convert-legacy-files'."
4477 (insert (mapconcat 'eval calendar-date-display-form "") 4477 (insert (mapconcat 'eval calendar-date-display-form "")
4478 (when time (concat " " time))))) 4478 (when time (concat " " time)))))
4479 4479
4480(defun todos-convert-legacy-files () 4480(defun todo-convert-legacy-files ()
4481 "Convert legacy Todo files to the current Todos format. 4481 "Convert legacy Todo files to the current Todo format.
4482The old-style files named by the variables `todo-file-do' and 4482The old-style files named by the variables `todo-file-do' and
4483`todo-file-done' from the old package are converted to the new 4483`todo-file-done' from the old package are converted to the new
4484format and saved (the latter as a Todos Archive file) with a new 4484format and saved (the latter as a Todo Archive file) with a new
4485name in `todos-directory'. See also the documentation string of 4485name in `todo-directory'. See also the documentation string of
4486`todos-legacy-date-time-regexp' for further details." 4486`todo-legacy-date-time-regexp' for further details."
4487 (interactive) 4487 (interactive)
4488 (if todos-file-buffers 4488 (if todo-file-buffers
4489 (message "Before converting you must kill all todo file buffers") 4489 (message "Before converting you must kill all todo file buffers")
4490 ;; Before loading legacy code we have to void symbols whose names 4490 ;; Before loading legacy code we have to void symbols whose names
4491 ;; are the same in the old and new versions, so use placeholders 4491 ;; are the same in the old and new versions, so use placeholders
4492 ;; during conversion and restore them afterwards. 4492 ;; during conversion and restore them afterwards.
4493 (let ((todos-categories-tem todos-categories) 4493 (let ((todo-categories-tem todo-categories)
4494 (todos-prefix-tem todos-prefix) 4494 (todo-prefix-tem todo-prefix)
4495 (todos-category-beg-tem todos-category-beg)) 4495 (todo-category-beg-tem todo-category-beg))
4496 (fset 'todos-mode-tem 'todos-mode) 4496 ;; (fset 'todo-mode-tem 'todo-mode)
4497 (makunbound 'todos-categories) 4497 (makunbound 'todo-categories)
4498 (makunbound 'todos-prefix) 4498 (makunbound 'todo-prefix)
4499 (makunbound 'todos-category-beg) 4499 (makunbound 'todo-category-beg)
4500 (fmakunbound 'todos-mode) 4500 (fmakunbound 'todo-mode)
4501 (when (eq this-command 'todos-convert-legacy-files) 4501 (when (eq this-command 'todo-convert-legacy-files)
4502 ;; We can't use require because the feature provided by the 4502 ;; We can't use require because the feature provided by the
4503 ;; old version is the same as the new version's. 4503 ;; old version is the same as the new version's.
4504 (load "todo-mode")) 4504 (load "todo-mode"))
@@ -4517,20 +4517,20 @@ name in `todos-directory'. See also the documentation string of
4517 (while (not (eobp)) 4517 (while (not (eobp))
4518 (cond 4518 (cond
4519 ((looking-at (regexp-quote (concat todo-prefix todo-category-beg))) 4519 ((looking-at (regexp-quote (concat todo-prefix todo-category-beg)))
4520 (replace-match todos-category-beg-tem)) 4520 (replace-match todo-category-beg-tem))
4521 ((looking-at (regexp-quote todo-category-end)) 4521 ((looking-at (regexp-quote todo-category-end))
4522 (replace-match "")) 4522 (replace-match ""))
4523 ((looking-at (regexp-quote (concat todo-prefix " " 4523 ((looking-at (regexp-quote (concat todo-prefix " "
4524 todo-category-sep))) 4524 todo-category-sep)))
4525 (replace-match todos-category-done)) 4525 (replace-match todo-category-done))
4526 ((looking-at (concat (regexp-quote todo-prefix) " " 4526 ((looking-at (concat (regexp-quote todo-prefix) " "
4527 todos-legacy-date-time-regexp " " 4527 todo-legacy-date-time-regexp " "
4528 (regexp-quote todo-initials) ":")) 4528 (regexp-quote todo-initials) ":"))
4529 ;; FIXME: Should todo-initials be converted? That 4529 ;; FIXME: Should todo-initials be converted? That
4530 ;; would require changes to item insertion and editing. 4530 ;; would require changes to item insertion and editing.
4531 (todos-convert-legacy-date-time))) 4531 (todo-convert-legacy-date-time)))
4532 (forward-line)) 4532 (forward-line))
4533 (setq file (concat todos-directory 4533 (setq file (concat todo-directory
4534 (read-string 4534 (read-string
4535 (format "Save file as (default \"%s\"): " default) 4535 (format "Save file as (default \"%s\"): " default)
4536 nil nil default) 4536 nil nil default)
@@ -4538,9 +4538,9 @@ name in `todos-directory'. See also the documentation string of
4538 (write-region (point-min) (point-max) file nil 'nomessage nil t)) 4538 (write-region (point-min) (point-max) file nil 'nomessage nil t))
4539 (with-temp-buffer 4539 (with-temp-buffer
4540 (insert-file-contents file) 4540 (insert-file-contents file)
4541 (let* ((todos-category-beg todos-category-beg-tem) ; Used by t-m-c-l. 4541 (let* ((todo-category-beg todo-category-beg-tem) ; Used by t-m-c-l.
4542 (todos-categories (todos-make-categories-list t))) 4542 (todo-categories (todo-make-categories-list t)))
4543 (todos-update-categories-sexp)) 4543 (todo-update-categories-sexp))
4544 (write-region (point-min) (point-max) file nil 'nomessage)) 4544 (write-region (point-min) (point-max) file nil 'nomessage))
4545 ;; Convert `todo-file-done'. 4545 ;; Convert `todo-file-done'.
4546 (when (file-exists-p todo-file-done) 4546 (when (file-exists-p todo-file-done)
@@ -4550,23 +4550,23 @@ name in `todos-directory'. See also the documentation string of
4550 (end (make-marker)) 4550 (end (make-marker))
4551 cat cats comment item) 4551 cat cats comment item)
4552 (while (not (eobp)) 4552 (while (not (eobp))
4553 (when (looking-at todos-legacy-date-time-regexp) 4553 (when (looking-at todo-legacy-date-time-regexp)
4554 (set-marker beg (point)) 4554 (set-marker beg (point))
4555 (todos-convert-legacy-date-time) 4555 (todo-convert-legacy-date-time)
4556 (set-marker end (point)) 4556 (set-marker end (point))
4557 (goto-char beg) 4557 (goto-char beg)
4558 (insert "[" todos-done-string) 4558 (insert "[" todo-done-string)
4559 (goto-char end) 4559 (goto-char end)
4560 (insert "]") 4560 (insert "]")
4561 (forward-char) 4561 (forward-char)
4562 (when (looking-at todos-legacy-date-time-regexp) 4562 (when (looking-at todo-legacy-date-time-regexp)
4563 (todos-convert-legacy-date-time)) 4563 (todo-convert-legacy-date-time))
4564 (when (looking-at (concat " " 4564 (when (looking-at (concat " "
4565 (regexp-quote todo-initials) ":")) 4565 (regexp-quote todo-initials) ":"))
4566 ;; FIXME: Should todo-initials be converted? 4566 ;; FIXME: Should todo-initials be converted?
4567 (replace-match ""))) 4567 (replace-match "")))
4568 (if (re-search-forward 4568 (if (re-search-forward
4569 (concat "^" todos-legacy-date-time-regexp) nil t) 4569 (concat "^" todo-legacy-date-time-regexp) nil t)
4570 (goto-char (match-beginning 0)) 4570 (goto-char (match-beginning 0))
4571 (goto-char (point-max))) 4571 (goto-char (point-max)))
4572 (backward-char) 4572 (backward-char)
@@ -4580,13 +4580,13 @@ name in `todos-directory'. See also the documentation string of
4580 (when (looking-back "(\\(.*\\)) ") 4580 (when (looking-back "(\\(.*\\)) ")
4581 (setq comment (match-string 1)) 4581 (setq comment (match-string 1))
4582 (replace-match "") 4582 (replace-match "")
4583 (insert "[" todos-comment-string ": " comment "]")) 4583 (insert "[" todo-comment-string ": " comment "]"))
4584 (set-marker end (point)) 4584 (set-marker end (point))
4585 (if (member cat cats) 4585 (if (member cat cats)
4586 ;; If item is already in its category, leave it there. 4586 ;; If item is already in its category, leave it there.
4587 (unless (save-excursion 4587 (unless (save-excursion
4588 (re-search-backward 4588 (re-search-backward
4589 (concat "^" (regexp-quote todos-category-beg-tem) 4589 (concat "^" (regexp-quote todo-category-beg-tem)
4590 "\\(.*\\)$") nil t) 4590 "\\(.*\\)$") nil t)
4591 (string= (match-string 1) cat)) 4591 (string= (match-string 1) cat))
4592 ;; Else move it to its category. 4592 ;; Else move it to its category.
@@ -4595,12 +4595,12 @@ name in `todos-directory'. See also the documentation string of
4595 (set-marker beg (point)) 4595 (set-marker beg (point))
4596 (re-search-backward 4596 (re-search-backward
4597 (concat "^" 4597 (concat "^"
4598 (regexp-quote (concat todos-category-beg-tem cat)) 4598 (regexp-quote (concat todo-category-beg-tem cat))
4599 "$") 4599 "$")
4600 nil t) 4600 nil t)
4601 (forward-line) 4601 (forward-line)
4602 (if (re-search-forward 4602 (if (re-search-forward
4603 (concat "^" (regexp-quote todos-category-beg-tem) 4603 (concat "^" (regexp-quote todo-category-beg-tem)
4604 "\\(.*\\)$") nil t) 4604 "\\(.*\\)$") nil t)
4605 (progn (goto-char (match-beginning 0)) 4605 (progn (goto-char (match-beginning 0))
4606 (newline) 4606 (newline)
@@ -4610,8 +4610,8 @@ name in `todos-directory'. See also the documentation string of
4610 (goto-char beg)) 4610 (goto-char beg))
4611 (push cat cats) 4611 (push cat cats)
4612 (goto-char beg) 4612 (goto-char beg)
4613 (insert todos-category-beg-tem cat "\n\n" 4613 (insert todo-category-beg-tem cat "\n\n"
4614 todos-category-done "\n")) 4614 todo-category-done "\n"))
4615 (forward-line)) 4615 (forward-line))
4616 (set-marker beg nil) 4616 (set-marker beg nil)
4617 (set-marker end nil)) 4617 (set-marker end nil))
@@ -4619,15 +4619,15 @@ name in `todos-directory'. See also the documentation string of
4619 (write-region (point-min) (point-max) file nil 'nomessage nil t)) 4619 (write-region (point-min) (point-max) file nil 'nomessage nil t))
4620 (with-temp-buffer 4620 (with-temp-buffer
4621 (insert-file-contents file) 4621 (insert-file-contents file)
4622 (let* ((todos-category-beg todos-category-beg-tem) ; Used by t-m-c-l. 4622 (let* ((todo-category-beg todo-category-beg-tem) ; Used by t-m-c-l.
4623 (todos-categories (todos-make-categories-list t))) 4623 (todo-categories (todo-make-categories-list t)))
4624 (todos-update-categories-sexp)) 4624 (todo-update-categories-sexp))
4625 (write-region (point-min) (point-max) file nil 'nomessage) 4625 (write-region (point-min) (point-max) file nil 'nomessage)
4626 (setq archive-sexp (read (buffer-substring-no-properties 4626 (setq archive-sexp (read (buffer-substring-no-properties
4627 (line-beginning-position) 4627 (line-beginning-position)
4628 (line-end-position))))) 4628 (line-end-position)))))
4629 (setq file (concat (file-name-sans-extension file) ".todo")) 4629 (setq file (concat (file-name-sans-extension file) ".todo"))
4630 ;; Update categories sexp of converted Todos file again, adding 4630 ;; Update categories sexp of converted Todo file again, adding
4631 ;; counts of archived items. 4631 ;; counts of archived items.
4632 (with-temp-buffer 4632 (with-temp-buffer
4633 (insert-file-contents file) 4633 (insert-file-contents file)
@@ -4641,158 +4641,158 @@ name in `todos-directory'. See also the documentation string of
4641 (delete-region (line-beginning-position) (line-end-position)) 4641 (delete-region (line-beginning-position) (line-end-position))
4642 (prin1 sexp (current-buffer))) 4642 (prin1 sexp (current-buffer)))
4643 (write-region (point-min) (point-max) file nil 'nomessage))) 4643 (write-region (point-min) (point-max) file nil 'nomessage)))
4644 (todos-reevaluate-filelist-defcustoms) 4644 (todo-reevaluate-filelist-defcustoms)
4645 (message "Format conversion done.")) 4645 (message "Format conversion done."))
4646 (message "No legacy Todo file exists")) 4646 (message "No legacy Todo file exists"))
4647 ;; (setq todos-categories todos-categories-tem 4647 ;; (setq todo-categories todo-categories-tem
4648 ;; todos-prefix todos-prefix-tem 4648 ;; todo-prefix todo-prefix-tem
4649 ;; todos-category-beg todos-category-beg-tem) 4649 ;; todo-category-beg todo-category-beg-tem)
4650 ;; (fset 'todos-mode 'todos-mode-tem) 4650 ;; (fset 'todo-mode 'todo-mode-tem)
4651 ;; (makunbound 'todos-categories-tem) 4651 ;; (makunbound 'todo-categories-tem)
4652 ;; (makunbound 'todos-prefix-tem) 4652 ;; (makunbound 'todo-prefix-tem)
4653 ;; (makunbound 'todos-category-beg-tem) 4653 ;; (makunbound 'todo-category-beg-tem)
4654 ;; (fmakunbound 'todos-mode-tem) 4654 ;; (fmakunbound 'todo-mode-tem)
4655 (unload-feature 'todos) 4655 (unload-feature 'todo)
4656 (require 'todos)))) 4656 (require 'todo))))
4657 4657
4658;; ----------------------------------------------------------------------------- 4658;; -----------------------------------------------------------------------------
4659;;; Utility functions for Todos files, categories and items 4659;;; Utility functions for Todo files, categories and items
4660;; ----------------------------------------------------------------------------- 4660;; -----------------------------------------------------------------------------
4661 4661
4662(defun todos-absolute-file-name (name &optional type) 4662(defun todo-absolute-file-name (name &optional type)
4663 "Return the absolute file name of short Todos file NAME. 4663 "Return the absolute file name of short Todo file NAME.
4664With TYPE `archive' or `top' return the absolute file name of the 4664With TYPE `archive' or `top' return the absolute file name of the
4665short Todos Archive or Top Priorities file name, respectively." 4665short Todo Archive or Top Priorities file name, respectively."
4666 ;; NOP if there is no Todos file yet (i.e. don't concatenate nil). 4666 ;; NOP if there is no Todo file yet (i.e. don't concatenate nil).
4667 (when name 4667 (when name
4668 (file-truename 4668 (file-truename
4669 (concat todos-directory name 4669 (concat todo-directory name
4670 (cond ((eq type 'archive) ".toda") 4670 (cond ((eq type 'archive) ".toda")
4671 ((eq type 'top) ".todt") 4671 ((eq type 'top) ".todt")
4672 ((eq type 'diary) ".tody") 4672 ((eq type 'diary) ".tody")
4673 ((eq type 'regexp) ".todr") 4673 ((eq type 'regexp) ".todr")
4674 (t ".todo")))))) 4674 (t ".todo"))))))
4675 4675
4676(defun todos-category-number (cat) 4676(defun todo-category-number (cat)
4677 "Return the number of category CAT in this Todos file. 4677 "Return the number of category CAT in this Todo file.
4678The buffer-local variable `todos-category-number' holds this 4678The buffer-local variable `todo-category-number' holds this
4679number as its value." 4679number as its value."
4680 (let ((categories (mapcar 'car todos-categories))) 4680 (let ((categories (mapcar 'car todo-categories)))
4681 (setq todos-category-number 4681 (setq todo-category-number
4682 ;; Increment by one, so that the highest priority category in Todos 4682 ;; Increment by one, so that the highest priority category in Todo
4683 ;; Categories mode is numbered one rather than zero. 4683 ;; Categories mode is numbered one rather than zero.
4684 (1+ (- (length categories) 4684 (1+ (- (length categories)
4685 (length (member cat categories))))))) 4685 (length (member cat categories)))))))
4686 4686
4687(defun todos-current-category () 4687(defun todo-current-category ()
4688 "Return the name of the current category." 4688 "Return the name of the current category."
4689 (car (nth (1- todos-category-number) todos-categories))) 4689 (car (nth (1- todo-category-number) todo-categories)))
4690 4690
4691(defun todos-category-select () 4691(defun todo-category-select ()
4692 "Display the current category correctly." 4692 "Display the current category correctly."
4693 (let ((name (todos-current-category)) 4693 (let ((name (todo-current-category))
4694 cat-begin cat-end done-start done-sep-start done-end) 4694 cat-begin cat-end done-start done-sep-start done-end)
4695 (widen) 4695 (widen)
4696 (goto-char (point-min)) 4696 (goto-char (point-min))
4697 (re-search-forward 4697 (re-search-forward
4698 (concat "^" (regexp-quote (concat todos-category-beg name)) "$") nil t) 4698 (concat "^" (regexp-quote (concat todo-category-beg name)) "$") nil t)
4699 (setq cat-begin (1+ (line-end-position))) 4699 (setq cat-begin (1+ (line-end-position)))
4700 (setq cat-end (if (re-search-forward 4700 (setq cat-end (if (re-search-forward
4701 (concat "^" (regexp-quote todos-category-beg)) nil t) 4701 (concat "^" (regexp-quote todo-category-beg)) nil t)
4702 (match-beginning 0) 4702 (match-beginning 0)
4703 (point-max))) 4703 (point-max)))
4704 (setq mode-line-buffer-identification 4704 (setq mode-line-buffer-identification
4705 (funcall todos-mode-line-function name)) 4705 (funcall todo-mode-line-function name))
4706 (narrow-to-region cat-begin cat-end) 4706 (narrow-to-region cat-begin cat-end)
4707 (todos-prefix-overlays) 4707 (todo-prefix-overlays)
4708 (goto-char (point-min)) 4708 (goto-char (point-min))
4709 (if (re-search-forward (concat "\n\\(" (regexp-quote todos-category-done) 4709 (if (re-search-forward (concat "\n\\(" (regexp-quote todo-category-done)
4710 "\\)") nil t) 4710 "\\)") nil t)
4711 (progn 4711 (progn
4712 (setq done-start (match-beginning 0)) 4712 (setq done-start (match-beginning 0))
4713 (setq done-sep-start (match-beginning 1)) 4713 (setq done-sep-start (match-beginning 1))
4714 (setq done-end (match-end 0))) 4714 (setq done-end (match-end 0)))
4715 (error "Category %s is missing todos-category-done string" name)) 4715 (error "Category %s is missing todo-category-done string" name))
4716 (if todos-show-done-only 4716 (if todo-show-done-only
4717 (narrow-to-region (1+ done-end) (point-max)) 4717 (narrow-to-region (1+ done-end) (point-max))
4718 (when (and todos-show-with-done 4718 (when (and todo-show-with-done
4719 (re-search-forward todos-done-string-start nil t)) 4719 (re-search-forward todo-done-string-start nil t))
4720 ;; Now we want to see the done items, so reset displayed end to end of 4720 ;; Now we want to see the done items, so reset displayed end to end of
4721 ;; done items. 4721 ;; done items.
4722 (setq done-start cat-end) 4722 (setq done-start cat-end)
4723 ;; Make display overlay for done items separator string, unless there 4723 ;; Make display overlay for done items separator string, unless there
4724 ;; already is one. 4724 ;; already is one.
4725 (let* ((done-sep todos-done-separator) 4725 (let* ((done-sep todo-done-separator)
4726 (ov (progn (goto-char done-sep-start) 4726 (ov (progn (goto-char done-sep-start)
4727 (todos-get-overlay 'separator)))) 4727 (todo-get-overlay 'separator))))
4728 (unless ov 4728 (unless ov
4729 (setq ov (make-overlay done-sep-start done-end)) 4729 (setq ov (make-overlay done-sep-start done-end))
4730 (overlay-put ov 'todos 'separator) 4730 (overlay-put ov 'todo 'separator)
4731 (overlay-put ov 'display done-sep)))) 4731 (overlay-put ov 'display done-sep))))
4732 (narrow-to-region (point-min) done-start) 4732 (narrow-to-region (point-min) done-start)
4733 ;; Loading this from todos-mode, or adding it to the mode hook, causes 4733 ;; Loading this from todo-mode, or adding it to the mode hook, causes
4734 ;; Emacs to hang in todos-item-start, at (looking-at todos-item-start). 4734 ;; Emacs to hang in todo-item-start, at (looking-at todo-item-start).
4735 (when todos-highlight-item 4735 (when todo-highlight-item
4736 (require 'hl-line) 4736 (require 'hl-line)
4737 (hl-line-mode 1))))) 4737 (hl-line-mode 1)))))
4738 4738
4739(defun todos-get-count (type &optional category) 4739(defun todo-get-count (type &optional category)
4740 "Return count of TYPE items in CATEGORY. 4740 "Return count of TYPE items in CATEGORY.
4741If CATEGORY is nil, default to the current category." 4741If CATEGORY is nil, default to the current category."
4742 (let* ((cat (or category (todos-current-category))) 4742 (let* ((cat (or category (todo-current-category)))
4743 (counts (cdr (assoc cat todos-categories))) 4743 (counts (cdr (assoc cat todo-categories)))
4744 (idx (cond ((eq type 'todo) 0) 4744 (idx (cond ((eq type 'todo) 0)
4745 ((eq type 'diary) 1) 4745 ((eq type 'diary) 1)
4746 ((eq type 'done) 2) 4746 ((eq type 'done) 2)
4747 ((eq type 'archived) 3)))) 4747 ((eq type 'archived) 3))))
4748 (aref counts idx))) 4748 (aref counts idx)))
4749 4749
4750(defun todos-update-count (type increment &optional category) 4750(defun todo-update-count (type increment &optional category)
4751 "Change count of TYPE items in CATEGORY by integer INCREMENT. 4751 "Change count of TYPE items in CATEGORY by integer INCREMENT.
4752With nil or omitted CATEGORY, default to the current category." 4752With nil or omitted CATEGORY, default to the current category."
4753 (let* ((cat (or category (todos-current-category))) 4753 (let* ((cat (or category (todo-current-category)))
4754 (counts (cdr (assoc cat todos-categories))) 4754 (counts (cdr (assoc cat todo-categories)))
4755 (idx (cond ((eq type 'todo) 0) 4755 (idx (cond ((eq type 'todo) 0)
4756 ((eq type 'diary) 1) 4756 ((eq type 'diary) 1)
4757 ((eq type 'done) 2) 4757 ((eq type 'done) 2)
4758 ((eq type 'archived) 3)))) 4758 ((eq type 'archived) 3))))
4759 (aset counts idx (+ increment (aref counts idx))))) 4759 (aset counts idx (+ increment (aref counts idx)))))
4760 4760
4761(defun todos-set-categories () 4761(defun todo-set-categories ()
4762 "Set `todos-categories' from the sexp at the top of the file." 4762 "Set `todo-categories' from the sexp at the top of the file."
4763 ;; New archive files created by `todos-move-category' are empty, which would 4763 ;; New archive files created by `todo-move-category' are empty, which would
4764 ;; make the sexp test fail and raise an error, so in this case we skip it. 4764 ;; make the sexp test fail and raise an error, so in this case we skip it.
4765 (unless (zerop (buffer-size)) 4765 (unless (zerop (buffer-size))
4766 (save-excursion 4766 (save-excursion
4767 (save-restriction 4767 (save-restriction
4768 (widen) 4768 (widen)
4769 (goto-char (point-min)) 4769 (goto-char (point-min))
4770 (setq todos-categories 4770 (setq todo-categories
4771 (if (looking-at "\(\(\"") 4771 (if (looking-at "\(\(\"")
4772 (read (buffer-substring-no-properties 4772 (read (buffer-substring-no-properties
4773 (line-beginning-position) 4773 (line-beginning-position)
4774 (line-end-position))) 4774 (line-end-position)))
4775 (error "Invalid or missing todos-categories sexp"))))))) 4775 (error "Invalid or missing todo-categories sexp")))))))
4776 4776
4777(defun todos-update-categories-sexp () 4777(defun todo-update-categories-sexp ()
4778 "Update the `todos-categories' sexp at the top of the file." 4778 "Update the `todo-categories' sexp at the top of the file."
4779 (let (buffer-read-only) 4779 (let (buffer-read-only)
4780 (save-excursion 4780 (save-excursion
4781 (save-restriction 4781 (save-restriction
4782 (widen) 4782 (widen)
4783 (goto-char (point-min)) 4783 (goto-char (point-min))
4784 (if (looking-at (concat "^" (regexp-quote todos-category-beg))) 4784 (if (looking-at (concat "^" (regexp-quote todo-category-beg)))
4785 (progn (newline) (goto-char (point-min)) ; Make space for sexp. 4785 (progn (newline) (goto-char (point-min)) ; Make space for sexp.
4786 (setq todos-categories (todos-make-categories-list t))) 4786 (setq todo-categories (todo-make-categories-list t)))
4787 (delete-region (line-beginning-position) (line-end-position))) 4787 (delete-region (line-beginning-position) (line-end-position)))
4788 (prin1 todos-categories (current-buffer)))))) 4788 (prin1 todo-categories (current-buffer))))))
4789 4789
4790(defun todos-make-categories-list (&optional force) 4790(defun todo-make-categories-list (&optional force)
4791 "Return an alist of Todos categories and their item counts. 4791 "Return an alist of Todo categories and their item counts.
4792With non-nil argument FORCE parse the entire file to build the 4792With non-nil argument FORCE parse the entire file to build the
4793list; otherwise, get the value by reading the sexp at the top of 4793list; otherwise, get the value by reading the sexp at the top of
4794the file." 4794the file."
4795 (setq todos-categories nil) 4795 (setq todo-categories nil)
4796 (save-excursion 4796 (save-excursion
4797 (save-restriction 4797 (save-restriction
4798 (widen) 4798 (widen)
@@ -4800,125 +4800,125 @@ the file."
4800 (let (counts cat archive) 4800 (let (counts cat archive)
4801 ;; If the file is a todo file and has archived items, identify the 4801 ;; If the file is a todo file and has archived items, identify the
4802 ;; archive, in order to count its items. But skip this with 4802 ;; archive, in order to count its items. But skip this with
4803 ;; `todos-convert-legacy-files', since that converts filed items to 4803 ;; `todo-convert-legacy-files', since that converts filed items to
4804 ;; archived items. 4804 ;; archived items.
4805 (when buffer-file-name ; During conversion there is no file yet. 4805 (when buffer-file-name ; During conversion there is no file yet.
4806 ;; If the file is an archive, it doesn't have an archive. 4806 ;; If the file is an archive, it doesn't have an archive.
4807 (unless (member (file-truename buffer-file-name) 4807 (unless (member (file-truename buffer-file-name)
4808 (funcall todos-files-function t)) 4808 (funcall todo-files-function t))
4809 (setq archive (concat (file-name-sans-extension 4809 (setq archive (concat (file-name-sans-extension
4810 todos-current-todos-file) ".toda")))) 4810 todo-current-todo-file) ".toda"))))
4811 (while (not (eobp)) 4811 (while (not (eobp))
4812 (cond ((looking-at (concat (regexp-quote todos-category-beg) 4812 (cond ((looking-at (concat (regexp-quote todo-category-beg)
4813 "\\(.*\\)\n")) 4813 "\\(.*\\)\n"))
4814 (setq cat (match-string-no-properties 1)) 4814 (setq cat (match-string-no-properties 1))
4815 ;; Counts for each category: [todo diary done archive] 4815 ;; Counts for each category: [todo diary done archive]
4816 (setq counts (make-vector 4 0)) 4816 (setq counts (make-vector 4 0))
4817 (setq todos-categories 4817 (setq todo-categories
4818 (append todos-categories (list (cons cat counts)))) 4818 (append todo-categories (list (cons cat counts))))
4819 ;; Add archived item count to the todo file item counts. 4819 ;; Add archived item count to the todo file item counts.
4820 ;; Make sure to include newly created archives, e.g. due to 4820 ;; Make sure to include newly created archives, e.g. due to
4821 ;; todos-move-category. 4821 ;; todo-move-category.
4822 (when (member archive (funcall todos-files-function t)) 4822 (when (member archive (funcall todo-files-function t))
4823 (let ((archive-count 0)) 4823 (let ((archive-count 0))
4824 (with-current-buffer (find-file-noselect archive) 4824 (with-current-buffer (find-file-noselect archive)
4825 (widen) 4825 (widen)
4826 (goto-char (point-min)) 4826 (goto-char (point-min))
4827 (when (re-search-forward 4827 (when (re-search-forward
4828 (concat "^" (regexp-quote todos-category-beg) 4828 (concat "^" (regexp-quote todo-category-beg)
4829 cat "$") 4829 cat "$")
4830 (point-max) t) 4830 (point-max) t)
4831 (forward-line) 4831 (forward-line)
4832 (while (not (or (looking-at 4832 (while (not (or (looking-at
4833 (concat 4833 (concat
4834 (regexp-quote todos-category-beg) 4834 (regexp-quote todo-category-beg)
4835 "\\(.*\\)\n")) 4835 "\\(.*\\)\n"))
4836 (eobp))) 4836 (eobp)))
4837 (when (looking-at todos-done-string-start) 4837 (when (looking-at todo-done-string-start)
4838 (setq archive-count (1+ archive-count))) 4838 (setq archive-count (1+ archive-count)))
4839 (forward-line)))) 4839 (forward-line))))
4840 (todos-update-count 'archived archive-count cat)))) 4840 (todo-update-count 'archived archive-count cat))))
4841 ((looking-at todos-done-string-start) 4841 ((looking-at todo-done-string-start)
4842 (todos-update-count 'done 1 cat)) 4842 (todo-update-count 'done 1 cat))
4843 ((looking-at (concat "^\\(" 4843 ((looking-at (concat "^\\("
4844 (regexp-quote diary-nonmarking-symbol) 4844 (regexp-quote diary-nonmarking-symbol)
4845 "\\)?" todos-date-pattern)) 4845 "\\)?" todo-date-pattern))
4846 (todos-update-count 'diary 1 cat) 4846 (todo-update-count 'diary 1 cat)
4847 (todos-update-count 'todo 1 cat)) 4847 (todo-update-count 'todo 1 cat))
4848 ((looking-at (concat todos-date-string-start todos-date-pattern)) 4848 ((looking-at (concat todo-date-string-start todo-date-pattern))
4849 (todos-update-count 'todo 1 cat)) 4849 (todo-update-count 'todo 1 cat))
4850 ;; If first line is todos-categories list, use it and end loop 4850 ;; If first line is todo-categories list, use it and end loop
4851 ;; -- unless FORCEd to scan whole file. 4851 ;; -- unless FORCEd to scan whole file.
4852 ((bobp) 4852 ((bobp)
4853 (unless force 4853 (unless force
4854 (setq todos-categories (read (buffer-substring-no-properties 4854 (setq todo-categories (read (buffer-substring-no-properties
4855 (line-beginning-position) 4855 (line-beginning-position)
4856 (line-end-position)))) 4856 (line-end-position))))
4857 (goto-char (1- (point-max)))))) 4857 (goto-char (1- (point-max))))))
4858 (forward-line))))) 4858 (forward-line)))))
4859 todos-categories) 4859 todo-categories)
4860 4860
4861(defun todos-repair-categories-sexp () 4861(defun todo-repair-categories-sexp ()
4862 "Repair corrupt Todos categories sexp. 4862 "Repair corrupt Todo categories sexp.
4863This should only be needed as a consequence of careless manual 4863This should only be needed as a consequence of careless manual
4864editing or a bug in todos.el. 4864editing or a bug in todo.el.
4865 4865
4866*Warning*: Calling this command restores the category order to 4866*Warning*: Calling this command restores the category order to
4867the list element order in the Todos categories sexp, so any order 4867the list element order in the Todo categories sexp, so any order
4868changes made in Todos Categories mode will have to be made again." 4868changes made in Todo Categories mode will have to be made again."
4869 (interactive) 4869 (interactive)
4870 (let ((todos-categories (todos-make-categories-list t))) 4870 (let ((todo-categories (todo-make-categories-list t)))
4871 (todos-update-categories-sexp))) 4871 (todo-update-categories-sexp)))
4872 4872
4873(defun todos-check-format () 4873(defun todo-check-format ()
4874 "Signal an error if the current Todos file is ill-formatted. 4874 "Signal an error if the current Todo file is ill-formatted.
4875Otherwise return t. Display a message if the file is well-formed 4875Otherwise return t. Display a message if the file is well-formed
4876but the categories sexp differs from the current value of 4876but the categories sexp differs from the current value of
4877`todos-categories'." 4877`todo-categories'."
4878 (save-excursion 4878 (save-excursion
4879 (save-restriction 4879 (save-restriction
4880 (widen) 4880 (widen)
4881 (goto-char (point-min)) 4881 (goto-char (point-min))
4882 (let* ((cats (prin1-to-string todos-categories)) 4882 (let* ((cats (prin1-to-string todo-categories))
4883 (ssexp (buffer-substring-no-properties (line-beginning-position) 4883 (ssexp (buffer-substring-no-properties (line-beginning-position)
4884 (line-end-position))) 4884 (line-end-position)))
4885 (sexp (read ssexp))) 4885 (sexp (read ssexp)))
4886 ;; Check the first line for `todos-categories' sexp. 4886 ;; Check the first line for `todo-categories' sexp.
4887 (dolist (c sexp) 4887 (dolist (c sexp)
4888 (let ((v (cdr c))) 4888 (let ((v (cdr c)))
4889 (unless (and (stringp (car c)) 4889 (unless (and (stringp (car c))
4890 (vectorp v) 4890 (vectorp v)
4891 (= 4 (length v))) 4891 (= 4 (length v)))
4892 (user-error "Invalid or missing todos-categories sexp")))) 4892 (user-error "Invalid or missing todo-categories sexp"))))
4893 (forward-line) 4893 (forward-line)
4894 ;; Check well-formedness of categories. 4894 ;; Check well-formedness of categories.
4895 (let ((legit (concat 4895 (let ((legit (concat
4896 "\\(^" (regexp-quote todos-category-beg) "\\)" 4896 "\\(^" (regexp-quote todo-category-beg) "\\)"
4897 "\\|\\(" todos-date-string-start todos-date-pattern "\\)" 4897 "\\|\\(" todo-date-string-start todo-date-pattern "\\)"
4898 "\\|\\(^[ \t]+[^ \t]*\\)" 4898 "\\|\\(^[ \t]+[^ \t]*\\)"
4899 "\\|^$" 4899 "\\|^$"
4900 "\\|\\(^" (regexp-quote todos-category-done) "\\)" 4900 "\\|\\(^" (regexp-quote todo-category-done) "\\)"
4901 "\\|\\(" todos-done-string-start "\\)"))) 4901 "\\|\\(" todo-done-string-start "\\)")))
4902 (while (not (eobp)) 4902 (while (not (eobp))
4903 (unless (looking-at legit) 4903 (unless (looking-at legit)
4904 (user-error "Illegitimate Todos file format at line %d" 4904 (user-error "Illegitimate Todo file format at line %d"
4905 (line-number-at-pos (point)))) 4905 (line-number-at-pos (point))))
4906 (forward-line))) 4906 (forward-line)))
4907 ;; Warn user if categories sexp has changed. 4907 ;; Warn user if categories sexp has changed.
4908 (unless (string= ssexp cats) 4908 (unless (string= ssexp cats)
4909 (message (concat "The sexp at the beginning of the file differs " 4909 (message (concat "The sexp at the beginning of the file differs "
4910 "from the value of `todos-categories.\n" 4910 "from the value of `todo-categories.\n"
4911 "If the sexp is wrong, you can fix it with " 4911 "If the sexp is wrong, you can fix it with "
4912 "M-x todos-repair-categories-sexp,\n" 4912 "M-x todo-repair-categories-sexp,\n"
4913 "but note this reverts any changes you have " 4913 "but note this reverts any changes you have "
4914 "made in the order of the categories.")))))) 4914 "made in the order of the categories."))))))
4915 t) 4915 t)
4916 4916
4917(defun todos-item-start () 4917(defun todo-item-start ()
4918 "Move to start of current Todos item and return its position." 4918 "Move to start of current Todo item and return its position."
4919 (unless (or 4919 (unless (or
4920 ;; Buffer is empty (invocation possible e.g. via todos-forward-item 4920 ;; Buffer is empty (invocation possible e.g. via todo-forward-item
4921 ;; from todos-filter-items when processing category with no todo 4921 ;; from todo-filter-items when processing category with no todo
4922 ;; items). 4922 ;; items).
4923 (eq (point-min) (point-max)) 4923 (eq (point-min) (point-max))
4924 ;; Point is on the empty line below category's last todo item... 4924 ;; Point is on the empty line below category's last todo item...
@@ -4927,19 +4927,19 @@ but the categories sexp differs from the current value of
4927 (save-excursion ; ...or done items are visible. 4927 (save-excursion ; ...or done items are visible.
4928 (forward-line) 4928 (forward-line)
4929 (looking-at (concat "^" 4929 (looking-at (concat "^"
4930 (regexp-quote todos-category-done)))))) 4930 (regexp-quote todo-category-done))))))
4931 ;; Buffer is widened. 4931 ;; Buffer is widened.
4932 (looking-at (regexp-quote todos-category-beg))) 4932 (looking-at (regexp-quote todo-category-beg)))
4933 (goto-char (line-beginning-position)) 4933 (goto-char (line-beginning-position))
4934 (while (not (looking-at todos-item-start)) 4934 (while (not (looking-at todo-item-start))
4935 (forward-line -1)) 4935 (forward-line -1))
4936 (point))) 4936 (point)))
4937 4937
4938(defun todos-item-end () 4938(defun todo-item-end ()
4939 "Move to end of current Todos item and return its position." 4939 "Move to end of current Todo item and return its position."
4940 ;; Items cannot end with a blank line. 4940 ;; Items cannot end with a blank line.
4941 (unless (looking-at "^$") 4941 (unless (looking-at "^$")
4942 (let* ((done (todos-done-item-p)) 4942 (let* ((done (todo-done-item-p))
4943 (to-lim nil) 4943 (to-lim nil)
4944 ;; For todo items, end is before the done items section, for done 4944 ;; For todo items, end is before the done items section, for done
4945 ;; items, end is before the next category. If these limits are 4945 ;; items, end is before the next category. If these limits are
@@ -4947,13 +4947,13 @@ but the categories sexp differs from the current value of
4947 (lim (if (save-excursion 4947 (lim (if (save-excursion
4948 (re-search-forward 4948 (re-search-forward
4949 (concat "^" (regexp-quote (if done 4949 (concat "^" (regexp-quote (if done
4950 todos-category-beg 4950 todo-category-beg
4951 todos-category-done))) 4951 todo-category-done)))
4952 nil t)) 4952 nil t))
4953 (progn (setq to-lim t) (match-beginning 0)) 4953 (progn (setq to-lim t) (match-beginning 0))
4954 (point-max)))) 4954 (point-max))))
4955 (when (bolp) (forward-char)) ; Find start of next item. 4955 (when (bolp) (forward-char)) ; Find start of next item.
4956 (goto-char (if (re-search-forward todos-item-start lim t) 4956 (goto-char (if (re-search-forward todo-item-start lim t)
4957 (match-beginning 0) 4957 (match-beginning 0)
4958 (if to-lim lim (point-max)))) 4958 (if to-lim lim (point-max))))
4959 ;; For last todo item, skip back over the empty line before the done 4959 ;; For last todo item, skip back over the empty line before the done
@@ -4961,27 +4961,27 @@ but the categories sexp differs from the current value of
4961 (backward-char (when (and to-lim (not done) (eq (point) lim)) 2)) 4961 (backward-char (when (and to-lim (not done) (eq (point) lim)) 2))
4962 (point)))) 4962 (point))))
4963 4963
4964(defun todos-item-string () 4964(defun todo-item-string ()
4965 "Return bare text of current item as a string." 4965 "Return bare text of current item as a string."
4966 (let ((opoint (point)) 4966 (let ((opoint (point))
4967 (start (todos-item-start)) 4967 (start (todo-item-start))
4968 (end (todos-item-end))) 4968 (end (todo-item-end)))
4969 (goto-char opoint) 4969 (goto-char opoint)
4970 (and start end (buffer-substring-no-properties start end)))) 4970 (and start end (buffer-substring-no-properties start end))))
4971 4971
4972(defun todos-forward-item (&optional count) 4972(defun todo-forward-item (&optional count)
4973 "Move point COUNT items down (by default, move down by one item)." 4973 "Move point COUNT items down (by default, move down by one item)."
4974 (let* ((not-done (not (or (todos-done-item-p) (looking-at "^$")))) 4974 (let* ((not-done (not (or (todo-done-item-p) (looking-at "^$"))))
4975 (start (line-end-position))) 4975 (start (line-end-position)))
4976 (goto-char start) 4976 (goto-char start)
4977 (if (re-search-forward todos-item-start nil t (or count 1)) 4977 (if (re-search-forward todo-item-start nil t (or count 1))
4978 (goto-char (match-beginning 0)) 4978 (goto-char (match-beginning 0))
4979 (goto-char (point-max))) 4979 (goto-char (point-max)))
4980 ;; If points advances by one from a todo to a done item, go back 4980 ;; If points advances by one from a todo to a done item, go back
4981 ;; to the space above todos-done-separator, since that is a 4981 ;; to the space above todo-done-separator, since that is a
4982 ;; legitimate place to insert an item. But skip this space if 4982 ;; legitimate place to insert an item. But skip this space if
4983 ;; count > 1, since that should only stop on an item. 4983 ;; count > 1, since that should only stop on an item.
4984 (when (and not-done (todos-done-item-p) (not count)) 4984 (when (and not-done (todo-done-item-p) (not count))
4985 ;; (if (or (not count) (= count 1)) 4985 ;; (if (or (not count) (= count 1))
4986 (re-search-backward "^$" start t))));) 4986 (re-search-backward "^$" start t))));)
4987 ;; The preceding sexp is insufficient when buffer is not narrowed, 4987 ;; The preceding sexp is insufficient when buffer is not narrowed,
@@ -4991,14 +4991,14 @@ but the categories sexp differs from the current value of
4991 ;; (let ((opoint) (point)) 4991 ;; (let ((opoint) (point))
4992 ;; (forward-line -1) 4992 ;; (forward-line -1)
4993 ;; (when (or (not count) (= count 1)) 4993 ;; (when (or (not count) (= count 1))
4994 ;; (cond ((looking-at (concat "^" (regexp-quote todos-category-beg))) 4994 ;; (cond ((looking-at (concat "^" (regexp-quote todo-category-beg)))
4995 ;; (forward-line -2)) 4995 ;; (forward-line -2))
4996 ;; ((looking-at (concat "^" (regexp-quote todos-category-done))) 4996 ;; ((looking-at (concat "^" (regexp-quote todo-category-done)))
4997 ;; (forward-line -1)) 4997 ;; (forward-line -1))
4998 ;; (t 4998 ;; (t
4999 ;; (goto-char opoint))))))) 4999 ;; (goto-char opoint)))))))
5000 5000
5001(defun todos-backward-item (&optional count) 5001(defun todo-backward-item (&optional count)
5002 "Move point up to start of item with next higher priority. 5002 "Move point up to start of item with next higher priority.
5003With positive numerical prefix COUNT, move point COUNT items 5003With positive numerical prefix COUNT, move point COUNT items
5004upward. 5004upward.
@@ -5009,37 +5009,37 @@ with point on the first done item and called with prefix 1, it
5009moves to the last todo item; but if called with point on the 5009moves to the last todo item; but if called with point on the
5010first done item without a prefix argument, it moves point the the 5010first done item without a prefix argument, it moves point the the
5011empty line above the done items separator." 5011empty line above the done items separator."
5012 (let* ((done (todos-done-item-p))) 5012 (let* ((done (todo-done-item-p)))
5013 (todos-item-start) 5013 (todo-item-start)
5014 (unless (bobp) 5014 (unless (bobp)
5015 (re-search-backward todos-item-start nil t (or count 1))) 5015 (re-search-backward todo-item-start nil t (or count 1)))
5016 ;; Unless this is a regexp filtered items buffer (which can contain 5016 ;; Unless this is a regexp filtered items buffer (which can contain
5017 ;; intermixed todo and done items), if points advances by one from a 5017 ;; intermixed todo and done items), if points advances by one from a
5018 ;; done to a todo item, go back to the space above 5018 ;; done to a todo item, go back to the space above
5019 ;; todos-done-separator, since that is a legitimate place to insert an 5019 ;; todo-done-separator, since that is a legitimate place to insert an
5020 ;; item. But skip this space if count > 1, since that should only 5020 ;; item. But skip this space if count > 1, since that should only
5021 ;; stop on an item. 5021 ;; stop on an item.
5022 (when (and done (not (todos-done-item-p)) (not count) 5022 (when (and done (not (todo-done-item-p)) (not count)
5023 ;(or (not count) (= count 1)) 5023 ;(or (not count) (= count 1))
5024 (not (equal (buffer-name) todos-regexp-items-buffer))) 5024 (not (equal (buffer-name) todo-regexp-items-buffer)))
5025 (re-search-forward (concat "^" (regexp-quote todos-category-done)) 5025 (re-search-forward (concat "^" (regexp-quote todo-category-done))
5026 nil t) 5026 nil t)
5027 (forward-line -1)))) 5027 (forward-line -1))))
5028 5028
5029(defun todos-remove-item () 5029(defun todo-remove-item ()
5030 "Internal function called in editing, deleting or moving items." 5030 "Internal function called in editing, deleting or moving items."
5031 (let* ((end (progn (todos-item-end) (1+ (point)))) 5031 (let* ((end (progn (todo-item-end) (1+ (point))))
5032 (beg (todos-item-start)) 5032 (beg (todo-item-start))
5033 (ov (todos-get-overlay 'prefix))) 5033 (ov (todo-get-overlay 'prefix)))
5034 (when ov (delete-overlay ov)) 5034 (when ov (delete-overlay ov))
5035 (delete-region beg end))) 5035 (delete-region beg end)))
5036 5036
5037(defun todos-diary-item-p () 5037(defun todo-diary-item-p ()
5038 "Return non-nil if item at point has diary entry format." 5038 "Return non-nil if item at point has diary entry format."
5039 (save-excursion 5039 (save-excursion
5040 (when (todos-item-string) ; Exclude empty lines. 5040 (when (todo-item-string) ; Exclude empty lines.
5041 (todos-item-start) 5041 (todo-item-start)
5042 (not (looking-at (regexp-quote todos-nondiary-start)))))) 5042 (not (looking-at (regexp-quote todo-nondiary-start))))))
5043 5043
5044;; This duplicates the item locating code from diary-goto-entry, but 5044;; This duplicates the item locating code from diary-goto-entry, but
5045;; without the marker code, to test whether the latter is dispensible. 5045;; without the marker code, to test whether the latter is dispensible.
@@ -5047,7 +5047,7 @@ empty line above the done items separator."
5047;; here can also be eliminated, leaving only the widening and category 5047;; here can also be eliminated, leaving only the widening and category
5048;; selection, and instead of :override advice :around can be used. 5048;; selection, and instead of :override advice :around can be used.
5049 5049
5050(defun todos-diary-goto-entry (button) 5050(defun todo-diary-goto-entry (button)
5051 "Jump to the diary entry for the BUTTON at point. 5051 "Jump to the diary entry for the BUTTON at point.
5052If the entry is a todo item, display its category properly. 5052If the entry is a todo item, display its category properly.
5053Overrides `diary-goto-entry'." 5053Overrides `diary-goto-entry'."
@@ -5059,59 +5059,59 @@ Overrides `diary-goto-entry'."
5059 (if (not (and (file-exists-p file) 5059 (if (not (and (file-exists-p file)
5060 (find-file-other-window file))) 5060 (find-file-other-window file)))
5061 (message "Unable to locate this diary entry") 5061 (message "Unable to locate this diary entry")
5062 (when (eq major-mode 'todos-mode) (widen)) 5062 (when (eq major-mode 'todo-mode) (widen))
5063 (goto-char (point-min)) 5063 (goto-char (point-min))
5064 (when (re-search-forward (format "%s.*\\(%s\\)" date content) nil t) 5064 (when (re-search-forward (format "%s.*\\(%s\\)" date content) nil t)
5065 (goto-char (match-beginning 1))) 5065 (goto-char (match-beginning 1)))
5066 ;; If it's a todo item, determine its category and display the 5066 ;; If it's a todo item, determine its category and display the
5067 ;; category properly. 5067 ;; category properly.
5068 (when (eq major-mode 'todos-mode) 5068 (when (eq major-mode 'todo-mode)
5069 (let ((opoint (point))) 5069 (let ((opoint (point)))
5070 (re-search-backward (concat "^" (regexp-quote todos-category-beg) 5070 (re-search-backward (concat "^" (regexp-quote todo-category-beg)
5071 "\\(.*\\)\n") nil t) 5071 "\\(.*\\)\n") nil t)
5072 (todos-category-number (match-string 1)) 5072 (todo-category-number (match-string 1))
5073 (todos-category-select) 5073 (todo-category-select)
5074 (goto-char opoint)))))) 5074 (goto-char opoint))))))
5075 5075
5076(add-function :override diary-goto-entry-function #'todos-diary-goto-entry) 5076(add-function :override diary-goto-entry-function #'todo-diary-goto-entry)
5077 5077
5078(defun todos-done-item-p () 5078(defun todo-done-item-p ()
5079 "Return non-nil if item at point is a done item." 5079 "Return non-nil if item at point is a done item."
5080 (save-excursion 5080 (save-excursion
5081 (todos-item-start) 5081 (todo-item-start)
5082 (looking-at todos-done-string-start))) 5082 (looking-at todo-done-string-start)))
5083 5083
5084(defun todos-done-item-section-p () 5084(defun todo-done-item-section-p ()
5085 "Return non-nil if point is in category's done items section." 5085 "Return non-nil if point is in category's done items section."
5086 (save-excursion 5086 (save-excursion
5087 (or (re-search-backward (concat "^" (regexp-quote todos-category-done)) 5087 (or (re-search-backward (concat "^" (regexp-quote todo-category-done))
5088 nil t) 5088 nil t)
5089 (progn (goto-char (point-min)) 5089 (progn (goto-char (point-min))
5090 (looking-at todos-done-string-start))))) 5090 (looking-at todo-done-string-start)))))
5091 5091
5092(defun todos-reset-done-separator (sep) 5092(defun todo-reset-done-separator (sep)
5093 "Replace existing overlays of done items separator string SEP." 5093 "Replace existing overlays of done items separator string SEP."
5094 (save-excursion 5094 (save-excursion
5095 (save-restriction 5095 (save-restriction
5096 (widen) 5096 (widen)
5097 (goto-char (point-min)) 5097 (goto-char (point-min))
5098 (while (re-search-forward 5098 (while (re-search-forward
5099 (concat "\n\\(" (regexp-quote todos-category-done) "\\)") nil t) 5099 (concat "\n\\(" (regexp-quote todo-category-done) "\\)") nil t)
5100 (let* ((beg (match-beginning 1)) 5100 (let* ((beg (match-beginning 1))
5101 (end (match-end 0)) 5101 (end (match-end 0))
5102 (ov (progn (goto-char beg) 5102 (ov (progn (goto-char beg)
5103 (todos-get-overlay 'separator))) 5103 (todo-get-overlay 'separator)))
5104 (old-sep (when ov (overlay-get ov 'display))) 5104 (old-sep (when ov (overlay-get ov 'display)))
5105 new-ov) 5105 new-ov)
5106 (when old-sep 5106 (when old-sep
5107 (unless (string= old-sep sep) 5107 (unless (string= old-sep sep)
5108 (setq new-ov (make-overlay beg end)) 5108 (setq new-ov (make-overlay beg end))
5109 (overlay-put new-ov 'todos 'separator) 5109 (overlay-put new-ov 'todo 'separator)
5110 (overlay-put new-ov 'display todos-done-separator) 5110 (overlay-put new-ov 'display todo-done-separator)
5111 (delete-overlay ov)))))))) 5111 (delete-overlay ov))))))))
5112 5112
5113(defun todos-get-overlay (val) 5113(defun todo-get-overlay (val)
5114 "Return the overlay at point whose `todos' property has value VAL." 5114 "Return the overlay at point whose `todo' property has value VAL."
5115 ;; Use overlays-in to find prefix overlays and check over two 5115 ;; Use overlays-in to find prefix overlays and check over two
5116 ;; positions to find done separator overlay. 5116 ;; positions to find done separator overlay.
5117 (let ((ovs (overlays-in (point) (1+ (point)))) 5117 (let ((ovs (overlays-in (point) (1+ (point))))
@@ -5119,85 +5119,85 @@ Overrides `diary-goto-entry'."
5119 (catch 'done 5119 (catch 'done
5120 (while ovs 5120 (while ovs
5121 (setq ov (pop ovs)) 5121 (setq ov (pop ovs))
5122 (when (eq (overlay-get ov 'todos) val) 5122 (when (eq (overlay-get ov 'todo) val)
5123 (throw 'done ov)))))) 5123 (throw 'done ov))))))
5124 5124
5125(defun todos-marked-item-p () 5125(defun todo-marked-item-p ()
5126 "Non-nil if this item begins with `todos-item-mark'. 5126 "Non-nil if this item begins with `todo-item-mark'.
5127In that case, return the item's prefix overlay." 5127In that case, return the item's prefix overlay."
5128 (let* ((ov (todos-get-overlay 'prefix)) 5128 (let* ((ov (todo-get-overlay 'prefix))
5129 ;; If an item insertion command is called on a Todos file 5129 ;; If an item insertion command is called on a Todo file
5130 ;; before it is visited, it has no prefix overlays yet, so 5130 ;; before it is visited, it has no prefix overlays yet, so
5131 ;; check for this. 5131 ;; check for this.
5132 (pref (when ov (overlay-get ov 'before-string))) 5132 (pref (when ov (overlay-get ov 'before-string)))
5133 (marked (when pref 5133 (marked (when pref
5134 (string-match (concat "^" (regexp-quote todos-item-mark)) 5134 (string-match (concat "^" (regexp-quote todo-item-mark))
5135 pref)))) 5135 pref))))
5136 (when marked ov))) 5136 (when marked ov)))
5137 5137
5138(defun todos-insert-with-overlays (item) 5138(defun todo-insert-with-overlays (item)
5139 "Insert ITEM at point and update prefix/priority number overlays." 5139 "Insert ITEM at point and update prefix/priority number overlays."
5140 (todos-item-start) 5140 (todo-item-start)
5141 ;; Insertion pushes item down but not its prefix overlay. When the 5141 ;; Insertion pushes item down but not its prefix overlay. When the
5142 ;; overlay includes a mark, this would now mark the inserted ITEM, 5142 ;; overlay includes a mark, this would now mark the inserted ITEM,
5143 ;; so move it to the pushed down item. 5143 ;; so move it to the pushed down item.
5144 (let ((ov (todos-get-overlay 'prefix)) 5144 (let ((ov (todo-get-overlay 'prefix))
5145 (marked (todos-marked-item-p))) 5145 (marked (todo-marked-item-p)))
5146 (insert item "\n") 5146 (insert item "\n")
5147 (when marked (move-overlay ov (point) (point)))) 5147 (when marked (move-overlay ov (point) (point))))
5148 (todos-backward-item) 5148 (todo-backward-item)
5149 (todos-prefix-overlays)) 5149 (todo-prefix-overlays))
5150 5150
5151(defun todos-prefix-overlays () 5151(defun todo-prefix-overlays ()
5152 "Update the prefix overlays of the current category's items. 5152 "Update the prefix overlays of the current category's items.
5153The overlay's value is the string `todos-prefix' or with non-nil 5153The overlay's value is the string `todo-prefix' or with non-nil
5154`todos-number-prefix' an integer in the sequence from 1 to 5154`todo-number-prefix' an integer in the sequence from 1 to
5155the number of todo or done items in the category indicating the 5155the number of todo or done items in the category indicating the
5156item's priority. Todo and done items are numbered independently 5156item's priority. Todo and done items are numbered independently
5157of each other." 5157of each other."
5158 (let ((num 0) 5158 (let ((num 0)
5159 (cat-tp (or (cdr (assoc-string 5159 (cat-tp (or (cdr (assoc-string
5160 (todos-current-category) 5160 (todo-current-category)
5161 (nth 2 (assoc-string todos-current-todos-file 5161 (nth 2 (assoc-string todo-current-todo-file
5162 todos-top-priorities-overrides)))) 5162 todo-top-priorities-overrides))))
5163 todos-top-priorities)) 5163 todo-top-priorities))
5164 done prefix) 5164 done prefix)
5165 (save-excursion 5165 (save-excursion
5166 (goto-char (point-min)) 5166 (goto-char (point-min))
5167 (while (not (eobp)) 5167 (while (not (eobp))
5168 (when (or (todos-date-string-matcher (line-end-position)) 5168 (when (or (todo-date-string-matcher (line-end-position))
5169 (todos-done-string-matcher (line-end-position))) 5169 (todo-done-string-matcher (line-end-position)))
5170 (goto-char (match-beginning 0)) 5170 (goto-char (match-beginning 0))
5171 (setq num (1+ num)) 5171 (setq num (1+ num))
5172 ;; Reset number to 1 for first done item. 5172 ;; Reset number to 1 for first done item.
5173 (when (and (eq major-mode 'todos-mode) 5173 (when (and (eq major-mode 'todo-mode)
5174 (looking-at todos-done-string-start) 5174 (looking-at todo-done-string-start)
5175 (looking-back (concat "^" 5175 (looking-back (concat "^"
5176 (regexp-quote todos-category-done) 5176 (regexp-quote todo-category-done)
5177 "\n"))) 5177 "\n")))
5178 (setq num 1 5178 (setq num 1
5179 done t)) 5179 done t))
5180 (setq prefix (concat (propertize 5180 (setq prefix (concat (propertize
5181 (if todos-number-prefix 5181 (if todo-number-prefix
5182 (number-to-string num) 5182 (number-to-string num)
5183 todos-prefix) 5183 todo-prefix)
5184 'face 5184 'face
5185 ;; Prefix of top priority items has a 5185 ;; Prefix of top priority items has a
5186 ;; distinct face in Todos mode. 5186 ;; distinct face in Todo mode.
5187 (if (and (eq major-mode 'todos-mode) 5187 (if (and (eq major-mode 'todo-mode)
5188 (not done) 5188 (not done)
5189 (<= num cat-tp)) 5189 (<= num cat-tp))
5190 'todos-top-priority 5190 'todo-top-priority
5191 'todos-prefix-string)) 5191 'todo-prefix-string))
5192 " ")) 5192 " "))
5193 (let ((ov (todos-get-overlay 'prefix)) 5193 (let ((ov (todo-get-overlay 'prefix))
5194 (marked (todos-marked-item-p))) 5194 (marked (todo-marked-item-p)))
5195 ;; Prefix overlay must be at a single position so its 5195 ;; Prefix overlay must be at a single position so its
5196 ;; bounds aren't changed when (re)moving an item. 5196 ;; bounds aren't changed when (re)moving an item.
5197 (unless ov (setq ov (make-overlay (point) (point)))) 5197 (unless ov (setq ov (make-overlay (point) (point))))
5198 (overlay-put ov 'todos 'prefix) 5198 (overlay-put ov 'todo 'prefix)
5199 (overlay-put ov 'before-string (if marked 5199 (overlay-put ov 'before-string (if marked
5200 (concat todos-item-mark prefix) 5200 (concat todo-item-mark prefix)
5201 prefix)))) 5201 prefix))))
5202 (forward-line))))) 5202 (forward-line)))))
5203 5203
@@ -5209,13 +5209,13 @@ of each other."
5209;; (http://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00423.html) 5209;; (http://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00423.html)
5210;; and kindly gave me permission to use it. 5210;; and kindly gave me permission to use it.
5211 5211
5212(defun todos-powerset (list) 5212(defun todo-powerset (list)
5213 "Return the powerset of LIST." 5213 "Return the powerset of LIST."
5214 (let ((powerset (list nil))) 5214 (let ((powerset (list nil)))
5215 (dolist (elt list (mapcar 'reverse powerset)) 5215 (dolist (elt list (mapcar 'reverse powerset))
5216 (nconc powerset (mapcar (apply-partially 'cons elt) powerset))))) 5216 (nconc powerset (mapcar (apply-partially 'cons elt) powerset)))))
5217 5217
5218(defun todos-gen-arglists (arglist) 5218(defun todo-gen-arglists (arglist)
5219 "Return list of lists of non-nil atoms produced from ARGLIST. 5219 "Return list of lists of non-nil atoms produced from ARGLIST.
5220The elements of ARGLIST may be atoms or lists." 5220The elements of ARGLIST may be atoms or lists."
5221 (let (arglists) 5221 (let (arglists)
@@ -5234,15 +5234,15 @@ The elements of ARGLIST may be atoms or lists."
5234 arg)))))) 5234 arg))))))
5235 (setq arglists (mapcar 'reverse (apply 'append (mapc 'car arglists)))))) 5235 (setq arglists (mapcar 'reverse (apply 'append (mapc 'car arglists))))))
5236 5236
5237(defvar todos-insertion-commands-args-genlist 5237(defvar todo-insertion-commands-args-genlist
5238 '(diary nonmarking (calendar date dayname) time (here region)) 5238 '(diary nonmarking (calendar date dayname) time (here region))
5239 "Generator list for argument lists of item insertion commands.") 5239 "Generator list for argument lists of item insertion commands.")
5240 5240
5241(defvar todos-insertion-commands-args 5241(defvar todo-insertion-commands-args
5242 (let ((argslist (todos-gen-arglists todos-insertion-commands-args-genlist)) 5242 (let ((argslist (todo-gen-arglists todo-insertion-commands-args-genlist))
5243 res new) 5243 res new)
5244 (setq res (cl-remove-duplicates 5244 (setq res (cl-remove-duplicates
5245 (apply 'append (mapcar 'todos-powerset argslist)) :test 'equal)) 5245 (apply 'append (mapcar 'todo-powerset argslist)) :test 'equal))
5246 (dolist (l res) 5246 (dolist (l res)
5247 (unless (= 5 (length l)) 5247 (unless (= 5 (length l))
5248 (let ((v (make-vector 5 nil)) elt) 5248 (let ((v (make-vector 5 nil)) elt)
@@ -5264,46 +5264,46 @@ The elements of ARGLIST may be atoms or lists."
5264 (setq l (append v nil)))) 5264 (setq l (append v nil))))
5265 (setq new (append new (list l)))) 5265 (setq new (append new (list l))))
5266 new) 5266 new)
5267 "List of all argument lists for Todos item insertion commands.") 5267 "List of all argument lists for Todo item insertion commands.")
5268 5268
5269(defun todos-insertion-command-name (arglist) 5269(defun todo-insertion-command-name (arglist)
5270 "Generate Todos item insertion command name from ARGLIST." 5270 "Generate Todo item insertion command name from ARGLIST."
5271 (replace-regexp-in-string 5271 (replace-regexp-in-string
5272 "-\\_>" "" 5272 "-\\_>" ""
5273 (replace-regexp-in-string 5273 (replace-regexp-in-string
5274 "-+" "-" 5274 "-+" "-"
5275 ;; (concat "todos-item-insert-" 5275 ;; (concat "todo-item-insert-"
5276 (concat "todos-insert-item-" 5276 (concat "todo-insert-item-"
5277 (mapconcat (lambda (e) (if e (symbol-name e))) arglist "-"))))) 5277 (mapconcat (lambda (e) (if e (symbol-name e))) arglist "-")))))
5278 5278
5279(defvar todos-insertion-commands-names 5279(defvar todo-insertion-commands-names
5280 (mapcar (lambda (l) 5280 (mapcar (lambda (l)
5281 (todos-insertion-command-name l)) 5281 (todo-insertion-command-name l))
5282 todos-insertion-commands-args) 5282 todo-insertion-commands-args)
5283 "List of names of Todos item insertion commands.") 5283 "List of names of Todo item insertion commands.")
5284 5284
5285(defmacro todos-define-insertion-command (&rest args) 5285(defmacro todo-define-insertion-command (&rest args)
5286 "Generate item insertion command definitions from ARGS." 5286 "Generate item insertion command definitions from ARGS."
5287 (let ((name (intern (todos-insertion-command-name args))) 5287 (let ((name (intern (todo-insertion-command-name args)))
5288 (arg0 (nth 0 args)) 5288 (arg0 (nth 0 args))
5289 (arg1 (nth 1 args)) 5289 (arg1 (nth 1 args))
5290 (arg2 (nth 2 args)) 5290 (arg2 (nth 2 args))
5291 (arg3 (nth 3 args)) 5291 (arg3 (nth 3 args))
5292 (arg4 (nth 4 args))) 5292 (arg4 (nth 4 args)))
5293 `(defun ,name (&optional arg &rest args) 5293 `(defun ,name (&optional arg &rest args)
5294 "Todos item insertion command generated from ARGS. 5294 "Todo item insertion command generated from ARGS.
5295For descriptions of the individual arguments, their values, and 5295For descriptions of the individual arguments, their values, and
5296their relation to key bindings, see `todos-basic-insert-item'." 5296their relation to key bindings, see `todo-basic-insert-item'."
5297 (interactive (list current-prefix-arg)) 5297 (interactive (list current-prefix-arg))
5298 (todos-basic-insert-item arg ',arg0 ',arg1 ',arg2 ',arg3 ',arg4)))) 5298 (todo-basic-insert-item arg ',arg0 ',arg1 ',arg2 ',arg3 ',arg4))))
5299 5299
5300(defvar todos-insertion-commands 5300(defvar todo-insertion-commands
5301 (mapcar (lambda (c) 5301 (mapcar (lambda (c)
5302 (eval `(todos-define-insertion-command ,@c))) 5302 (eval `(todo-define-insertion-command ,@c)))
5303 todos-insertion-commands-args) 5303 todo-insertion-commands-args)
5304 "List of Todos item insertion commands.") 5304 "List of Todo item insertion commands.")
5305 5305
5306(defvar todos-insertion-commands-arg-key-list 5306(defvar todo-insertion-commands-arg-key-list
5307 '(("diary" "y" "yy") 5307 '(("diary" "y" "yy")
5308 ("nonmarking" "k" "kk") 5308 ("nonmarking" "k" "kk")
5309 ("calendar" "c" "cc") 5309 ("calendar" "c" "cc")
@@ -5314,9 +5314,9 @@ their relation to key bindings, see `todos-basic-insert-item'."
5314 ("region" "r" "r")) 5314 ("region" "r" "r"))
5315 "List of mappings of insertion command arguments to key sequences.") 5315 "List of mappings of insertion command arguments to key sequences.")
5316 5316
5317(defun todos-insertion-key-bindings (map) 5317(defun todo-insertion-key-bindings (map)
5318 "Generate key binding definitions for item insertion keymap MAP." 5318 "Generate key binding definitions for item insertion keymap MAP."
5319 (dolist (c todos-insertion-commands) 5319 (dolist (c todo-insertion-commands)
5320 (let* ((key "") 5320 (let* ((key "")
5321 (cname (symbol-name c))) 5321 (cname (symbol-name c)))
5322 (mapc (lambda (l) 5322 (mapc (lambda (l)
@@ -5327,63 +5327,63 @@ their relation to key bindings, see `todos-basic-insert-item'."
5327 (setq key (concat key key2))) 5327 (setq key (concat key key2)))
5328 (if (string-match (concat (regexp-quote arg) ".+") cname) 5328 (if (string-match (concat (regexp-quote arg) ".+") cname)
5329 (setq key (concat key key1))))) 5329 (setq key (concat key key1)))))
5330 todos-insertion-commands-arg-key-list) 5330 todo-insertion-commands-arg-key-list)
5331 (if (string-match (concat (regexp-quote "todos-insert-item") "\\_>") cname) 5331 (if (string-match (concat (regexp-quote "todo-insert-item") "\\_>") cname)
5332 (setq key (concat key "i"))) 5332 (setq key (concat key "i")))
5333 (define-key map key c)))) 5333 (define-key map key c))))
5334 5334
5335;; ----------------------------------------------------------------------------- 5335;; -----------------------------------------------------------------------------
5336;;; Todos minibuffer utilities 5336;;; Todo minibuffer utilities
5337;; ----------------------------------------------------------------------------- 5337;; -----------------------------------------------------------------------------
5338 5338
5339(defcustom todos-y-with-space nil 5339(defcustom todo-y-with-space nil
5340 "Non-nil means allow SPC to affirm a \"y or n\" question." 5340 "Non-nil means allow SPC to affirm a \"y or n\" question."
5341 :type 'boolean 5341 :type 'boolean
5342 :group 'todos) 5342 :group 'todo)
5343 5343
5344(defun todos-y-or-n-p (prompt) 5344(defun todo-y-or-n-p (prompt)
5345 "Ask \"y or n\" question PROMPT and return t if answer is \"y\". 5345 "Ask \"y or n\" question PROMPT and return t if answer is \"y\".
5346Also return t if answer is \"Y\", but unlike `y-or-n-p', allow 5346Also return t if answer is \"Y\", but unlike `y-or-n-p', allow
5347SPC to affirm the question only if option `todos-y-with-space' is 5347SPC to affirm the question only if option `todo-y-with-space' is
5348non-nil." 5348non-nil."
5349 (unless todos-y-with-space 5349 (unless todo-y-with-space
5350 (define-key query-replace-map " " 'ignore)) 5350 (define-key query-replace-map " " 'ignore))
5351 (prog1 5351 (prog1
5352 (y-or-n-p prompt) 5352 (y-or-n-p prompt)
5353 (define-key query-replace-map " " 'act))) 5353 (define-key query-replace-map " " 'act)))
5354 5354
5355(defun todos-category-completions (&optional archive) 5355(defun todo-category-completions (&optional archive)
5356 "Return a list of completions for `todos-read-category'. 5356 "Return a list of completions for `todo-read-category'.
5357Each element of the list is a cons of a category name and the 5357Each element of the list is a cons of a category name and the
5358file or list of files (as short file names) it is in. The files 5358file or list of files (as short file names) it is in. The files
5359are either the current (or if there is none, the default) todo 5359are either the current (or if there is none, the default) todo
5360file plus the files listed in `todos-category-completions-files', 5360file plus the files listed in `todo-category-completions-files',
5361or, with non-nil ARCHIVE, the current archive file." 5361or, with non-nil ARCHIVE, the current archive file."
5362 (let* ((curfile (or todos-current-todos-file 5362 (let* ((curfile (or todo-current-todo-file
5363 (and todos-show-current-file 5363 (and todo-show-current-file
5364 todos-global-current-todos-file) 5364 todo-global-current-todo-file)
5365 (todos-absolute-file-name todos-default-todos-file))) 5365 (todo-absolute-file-name todo-default-todo-file)))
5366 (files (or (unless archive 5366 (files (or (unless archive
5367 (mapcar 'todos-absolute-file-name 5367 (mapcar 'todo-absolute-file-name
5368 todos-category-completions-files)) 5368 todo-category-completions-files))
5369 (list curfile))) 5369 (list curfile)))
5370 listall listf) 5370 listall listf)
5371 ;; If file was just added, it has no category completions. 5371 ;; If file was just added, it has no category completions.
5372 (unless (zerop (buffer-size (find-buffer-visiting curfile))) 5372 (unless (zerop (buffer-size (find-buffer-visiting curfile)))
5373 (unless (member curfile todos-archives) 5373 (unless (member curfile todo-archives)
5374 (add-to-list 'files curfile)) 5374 (add-to-list 'files curfile))
5375 (dolist (f files listall) 5375 (dolist (f files listall)
5376 (with-current-buffer (find-file-noselect f 'nowarn) 5376 (with-current-buffer (find-file-noselect f 'nowarn)
5377 ;; Ensure category is properly displayed in case user 5377 ;; Ensure category is properly displayed in case user
5378 ;; switches to file via a non-Todos command. And if done 5378 ;; switches to file via a non-Todo command. And if done
5379 ;; items in category are visible, keep them visible. 5379 ;; items in category are visible, keep them visible.
5380 (let ((done todos-show-with-done)) 5380 (let ((done todo-show-with-done))
5381 (when (> (buffer-size) (- (point-max) (point-min))) 5381 (when (> (buffer-size) (- (point-max) (point-min)))
5382 (save-excursion 5382 (save-excursion
5383 (goto-char (point-min)) 5383 (goto-char (point-min))
5384 (setq done (re-search-forward todos-done-string-start nil t)))) 5384 (setq done (re-search-forward todo-done-string-start nil t))))
5385 (let ((todos-show-with-done done)) 5385 (let ((todo-show-with-done done))
5386 (save-excursion (todos-category-select)))) 5386 (save-excursion (todo-category-select))))
5387 (save-excursion 5387 (save-excursion
5388 (save-restriction 5388 (save-restriction
5389 (widen) 5389 (widen)
@@ -5399,38 +5399,38 @@ or, with non-nil ARCHIVE, the current archive file."
5399 (push (cons cat f) listall)))) 5399 (push (cons cat f) listall))))
5400 listf))))) 5400 listf)))))
5401 5401
5402(defun todos-read-file-name (prompt &optional archive mustmatch) 5402(defun todo-read-file-name (prompt &optional archive mustmatch)
5403 "Choose and return the name of a Todos file, prompting with PROMPT. 5403 "Choose and return the name of a Todo file, prompting with PROMPT.
5404 5404
5405Show completions with TAB or SPC; the names are shown in short 5405Show completions with TAB or SPC; the names are shown in short
5406form but the absolute truename is returned. With non-nil ARCHIVE 5406form but the absolute truename is returned. With non-nil ARCHIVE
5407return the absolute truename of a Todos archive file. With non-nil 5407return the absolute truename of a Todo archive file. With non-nil
5408MUSTMATCH the name of an existing file must be chosen; 5408MUSTMATCH the name of an existing file must be chosen;
5409otherwise, a new file name is allowed." 5409otherwise, a new file name is allowed."
5410 (let* ((completion-ignore-case todos-completion-ignore-case) 5410 (let* ((completion-ignore-case todo-completion-ignore-case)
5411 (files (mapcar 'todos-short-file-name 5411 (files (mapcar 'todo-short-file-name
5412 (if archive todos-archives todos-files))) 5412 (if archive todo-archives todo-files)))
5413 (file (completing-read prompt files nil mustmatch nil nil 5413 (file (completing-read prompt files nil mustmatch nil nil
5414 (if files 5414 (if files
5415 ;; If user hit RET without 5415 ;; If user hit RET without
5416 ;; choosing a file, default to 5416 ;; choosing a file, default to
5417 ;; current or default file. 5417 ;; current or default file.
5418 (todos-short-file-name 5418 (todo-short-file-name
5419 (or todos-current-todos-file 5419 (or todo-current-todo-file
5420 (and todos-show-current-file 5420 (and todo-show-current-file
5421 todos-global-current-todos-file) 5421 todo-global-current-todo-file)
5422 (todos-absolute-file-name 5422 (todo-absolute-file-name
5423 todos-default-todos-file))) 5423 todo-default-todo-file)))
5424 ;; Trigger prompt for initial file. 5424 ;; Trigger prompt for initial file.
5425 "")))) 5425 ""))))
5426 (unless (file-exists-p todos-directory) 5426 (unless (file-exists-p todo-directory)
5427 (make-directory todos-directory)) 5427 (make-directory todo-directory))
5428 (unless mustmatch 5428 (unless mustmatch
5429 (setq file (todos-validate-name file 'file))) 5429 (setq file (todo-validate-name file 'file)))
5430 (setq file (file-truename (concat todos-directory file 5430 (setq file (file-truename (concat todo-directory file
5431 (if archive ".toda" ".todo")))))) 5431 (if archive ".toda" ".todo"))))))
5432 5432
5433(defun todos-read-category (prompt &optional match-type file) 5433(defun todo-read-category (prompt &optional match-type file)
5434 "Choose and return a category name, prompting with PROMPT. 5434 "Choose and return a category name, prompting with PROMPT.
5435Show completions for existing categories with TAB or SPC. 5435Show completions for existing categories with TAB or SPC.
5436 5436
@@ -5443,34 +5443,34 @@ valid category names are accepted.
5443 5443
5444With non-nil argument FILE prompt for a file and complete only 5444With non-nil argument FILE prompt for a file and complete only
5445against categories in that file; otherwise complete against all 5445against categories in that file; otherwise complete against all
5446categories from `todos-category-completions-files'." 5446categories from `todo-category-completions-files'."
5447 ;; Allow SPC to insert spaces, for adding new category names. 5447 ;; Allow SPC to insert spaces, for adding new category names.
5448 (let ((map minibuffer-local-completion-map)) 5448 (let ((map minibuffer-local-completion-map))
5449 (define-key map " " nil) 5449 (define-key map " " nil)
5450 (let* ((add (eq match-type 'add)) 5450 (let* ((add (eq match-type 'add))
5451 (archive (eq match-type 'archive)) 5451 (archive (eq match-type 'archive))
5452 (file0 (when (and file (> (length todos-files) 1)) 5452 (file0 (when (and file (> (length todo-files) 1))
5453 (todos-read-file-name (concat "Choose a" (if archive 5453 (todo-read-file-name (concat "Choose a" (if archive
5454 "n archive" 5454 "n archive"
5455 " todo") 5455 " todo")
5456 " file: ") archive t))) 5456 " file: ") archive t)))
5457 (completions (unless file0 (todos-category-completions archive))) 5457 (completions (unless file0 (todo-category-completions archive)))
5458 (categories (cond (file0 5458 (categories (cond (file0
5459 (with-current-buffer 5459 (with-current-buffer
5460 (find-file-noselect file0 'nowarn) 5460 (find-file-noselect file0 'nowarn)
5461 (let ((todos-current-todos-file file0)) 5461 (let ((todo-current-todo-file file0))
5462 todos-categories))) 5462 todo-categories)))
5463 ((and add (not file)) 5463 ((and add (not file))
5464 (with-current-buffer 5464 (with-current-buffer
5465 (find-file-noselect todos-current-todos-file) 5465 (find-file-noselect todo-current-todo-file)
5466 todos-categories)) 5466 todo-categories))
5467 (t 5467 (t
5468 completions))) 5468 completions)))
5469 (completion-ignore-case todos-completion-ignore-case) 5469 (completion-ignore-case todo-completion-ignore-case)
5470 (cat (completing-read prompt categories nil 5470 (cat (completing-read prompt categories nil
5471 (eq match-type 'todo) nil nil 5471 (eq match-type 'todo) nil nil
5472 ;; Unless we're adding a category via 5472 ;; Unless we're adding a category via
5473 ;; todos-add-category, set default 5473 ;; todo-add-category, set default
5474 ;; for existing categories to the 5474 ;; for existing categories to the
5475 ;; current category of the chosen 5475 ;; current category of the chosen
5476 ;; file or else of the current file. 5476 ;; file or else of the current file.
@@ -5478,10 +5478,10 @@ categories from `todos-category-completions-files'."
5478 (with-current-buffer 5478 (with-current-buffer
5479 (find-file-noselect 5479 (find-file-noselect
5480 (or file0 5480 (or file0
5481 todos-current-todos-file 5481 todo-current-todo-file
5482 (todos-absolute-file-name 5482 (todo-absolute-file-name
5483 todos-default-todos-file))) 5483 todo-default-todo-file)))
5484 (todos-current-category)) 5484 (todo-current-category))
5485 ;; Trigger prompt for initial category. 5485 ;; Trigger prompt for initial category.
5486 ""))) 5486 "")))
5487 (catfil (cdr (assoc cat completions))) 5487 (catfil (cdr (assoc cat completions)))
@@ -5492,42 +5492,42 @@ categories from `todos-category-completions-files'."
5492 (setq file0 (file-truename 5492 (setq file0 (file-truename
5493 (if (atom catfil) 5493 (if (atom catfil)
5494 catfil 5494 catfil
5495 (todos-absolute-file-name 5495 (todo-absolute-file-name
5496 (let ((files (mapcar 'todos-short-file-name catfil))) 5496 (let ((files (mapcar 'todo-short-file-name catfil)))
5497 (completing-read (format str cat) files))))))) 5497 (completing-read (format str cat) files)))))))
5498 ;; Default to the current file. 5498 ;; Default to the current file.
5499 (unless file0 (setq file0 todos-current-todos-file)) 5499 (unless file0 (setq file0 todo-current-todo-file))
5500 ;; First validate only a name passed interactively from 5500 ;; First validate only a name passed interactively from
5501 ;; todos-add-category, which must be of a nonexisting category. 5501 ;; todo-add-category, which must be of a nonexisting category.
5502 (unless (and (assoc cat categories) (not add)) 5502 (unless (and (assoc cat categories) (not add))
5503 ;; Validate only against completion categories. 5503 ;; Validate only against completion categories.
5504 (let ((todos-categories categories)) 5504 (let ((todo-categories categories))
5505 (setq cat (todos-validate-name cat 'category))) 5505 (setq cat (todo-validate-name cat 'category)))
5506 ;; When user enters a nonexisting category name by jumping or 5506 ;; When user enters a nonexisting category name by jumping or
5507 ;; moving, confirm that it should be added, then validate. 5507 ;; moving, confirm that it should be added, then validate.
5508 (unless add 5508 (unless add
5509 (if (todos-y-or-n-p (format "Add new category \"%s\" to file \"%s\"? " 5509 (if (todo-y-or-n-p (format "Add new category \"%s\" to file \"%s\"? "
5510 cat (todos-short-file-name file0))) 5510 cat (todo-short-file-name file0)))
5511 (progn 5511 (progn
5512 (when (assoc cat categories) 5512 (when (assoc cat categories)
5513 (let ((todos-categories categories)) 5513 (let ((todo-categories categories))
5514 (setq cat (todos-validate-name cat 'category)))) 5514 (setq cat (todo-validate-name cat 'category))))
5515 ;; Restore point and narrowing after adding new 5515 ;; Restore point and narrowing after adding new
5516 ;; category, to avoid moving to beginning of file when 5516 ;; category, to avoid moving to beginning of file when
5517 ;; moving marked items to a new category 5517 ;; moving marked items to a new category
5518 ;; (todos-move-item). 5518 ;; (todo-move-item).
5519 (save-excursion 5519 (save-excursion
5520 (save-restriction 5520 (save-restriction
5521 (todos-add-category file0 cat)))) 5521 (todo-add-category file0 cat))))
5522 ;; If we decide not to add a category, exit without returning. 5522 ;; If we decide not to add a category, exit without returning.
5523 (keyboard-quit)))) 5523 (keyboard-quit))))
5524 (cons cat file0)))) 5524 (cons cat file0))))
5525 5525
5526(defun todos-validate-name (name type) 5526(defun todo-validate-name (name type)
5527 "Prompt for new NAME for TYPE until it is valid, then return it. 5527 "Prompt for new NAME for TYPE until it is valid, then return it.
5528TYPE can be either of the symbols `file' or `category'." 5528TYPE can be either of the symbols `file' or `category'."
5529 (let ((categories todos-categories) 5529 (let ((categories todo-categories)
5530 (files (mapcar 'todos-short-file-name todos-files)) 5530 (files (mapcar 'todo-short-file-name todo-files))
5531 prompt) 5531 prompt)
5532 (while 5532 (while
5533 (and 5533 (and
@@ -5536,18 +5536,18 @@ TYPE can be either of the symbols `file' or `category'."
5536 (cond ((eq type 'file) 5536 (cond ((eq type 'file)
5537 (if files 5537 (if files
5538 "Enter a non-empty file name: " 5538 "Enter a non-empty file name: "
5539 ;; Empty string passed by todos-show to 5539 ;; Empty string passed by todo-show to
5540 ;; prompt for initial Todos file. 5540 ;; prompt for initial Todo file.
5541 (concat "Initial file name [" 5541 (concat "Initial file name ["
5542 todos-initial-file "]: "))) 5542 todo-initial-file "]: ")))
5543 ((eq type 'category) 5543 ((eq type 'category)
5544 (if categories 5544 (if categories
5545 "Enter a non-empty category name: " 5545 "Enter a non-empty category name: "
5546 ;; Empty string passed by todos-show to 5546 ;; Empty string passed by todo-show to
5547 ;; prompt for initial category of a new 5547 ;; prompt for initial category of a new
5548 ;; Todos file. 5548 ;; Todo file.
5549 (concat "Initial category name [" 5549 (concat "Initial category name ["
5550 todos-initial-category "]: ")))))) 5550 todo-initial-category "]: "))))))
5551 ((string-match "\\`\\s-+\\'" name) 5551 ((string-match "\\`\\s-+\\'" name)
5552 (setq prompt 5552 (setq prompt
5553 "Enter a name that does not contain only white space: ")) 5553 "Enter a name that does not contain only white space: "))
@@ -5566,12 +5566,12 @@ TYPE can be either of the symbols `file' or `category'."
5566 files 5566 files
5567 categories) 5567 categories)
5568 nil nil (if (eq type 'file) 5568 nil nil (if (eq type 'file)
5569 todos-initial-file 5569 todo-initial-file
5570 todos-initial-category)))))) 5570 todo-initial-category))))))
5571 name)) 5571 name))
5572 5572
5573;; Adapted from calendar-read-date and calendar-date-string. 5573;; Adapted from calendar-read-date and calendar-date-string.
5574(defun todos-read-date (&optional arg mo yr) 5574(defun todo-read-date (&optional arg mo yr)
5575 "Prompt for Gregorian date and return it in the current format. 5575 "Prompt for Gregorian date and return it in the current format.
5576 5576
5577With non-nil ARG, prompt for and return only the date component 5577With non-nil ARG, prompt for and return only the date component
@@ -5593,11 +5593,11 @@ number of the last the day of the month."
5593 (calendar-extract-year 5593 (calendar-extract-year
5594 (calendar-current-date))))))) 5594 (calendar-current-date)))))))
5595 (when (or (not arg) (eq arg 'month)) 5595 (when (or (not arg) (eq arg 'month))
5596 (let* ((marray todos-month-name-array) 5596 (let* ((marray todo-month-name-array)
5597 (mlist (append marray nil)) 5597 (mlist (append marray nil))
5598 (mabarray todos-month-abbrev-array) 5598 (mabarray todo-month-abbrev-array)
5599 (mablist (append mabarray nil)) 5599 (mablist (append mabarray nil))
5600 (completion-ignore-case todos-completion-ignore-case)) 5600 (completion-ignore-case todo-completion-ignore-case))
5601 (setq monthname (completing-read 5601 (setq monthname (completing-read
5602 "Month name (RET for current month, * for any month): " 5602 "Month name (RET for current month, * for any month): "
5603 ;; (mapcar 'list (append marray nil)) 5603 ;; (mapcar 'list (append marray nil))
@@ -5649,24 +5649,24 @@ number of the last the day of the month."
5649 monthname))) 5649 monthname)))
5650 (mapconcat 'eval calendar-date-display-form "")))) 5650 (mapconcat 'eval calendar-date-display-form ""))))
5651 5651
5652(defun todos-read-dayname () 5652(defun todo-read-dayname ()
5653 "Choose name of a day of the week with completion and return it." 5653 "Choose name of a day of the week with completion and return it."
5654 (let ((completion-ignore-case todos-completion-ignore-case)) 5654 (let ((completion-ignore-case todo-completion-ignore-case))
5655 (completing-read "Enter a day name: " 5655 (completing-read "Enter a day name: "
5656 (append calendar-day-name-array nil) 5656 (append calendar-day-name-array nil)
5657 nil t))) 5657 nil t)))
5658 5658
5659(defun todos-read-time () 5659(defun todo-read-time ()
5660 "Prompt for and return a valid clock time as a string. 5660 "Prompt for and return a valid clock time as a string.
5661 5661
5662Valid time strings are those matching `diary-time-regexp'. 5662Valid time strings are those matching `diary-time-regexp'.
5663Typing `<return>' at the prompt returns the current time, if the 5663Typing `<return>' at the prompt returns the current time, if the
5664user option `todos-always-add-time-string' is non-nil, otherwise 5664user option `todo-always-add-time-string' is non-nil, otherwise
5665the empty string (i.e., no time string)." 5665the empty string (i.e., no time string)."
5666 (let (valid answer) 5666 (let (valid answer)
5667 (while (not valid) 5667 (while (not valid)
5668 (setq answer (read-string "Enter a clock time: " nil nil 5668 (setq answer (read-string "Enter a clock time: " nil nil
5669 (when todos-always-add-time-string 5669 (when todo-always-add-time-string
5670 (substring (current-time-string) 11 16)))) 5670 (substring (current-time-string) 11 16))))
5671 (when (or (string= "" answer) 5671 (when (or (string= "" answer)
5672 (string-match diary-time-regexp answer)) 5672 (string-match diary-time-regexp answer))
@@ -5677,66 +5677,66 @@ the empty string (i.e., no time string)."
5677;;; Customization groups and utilities 5677;;; Customization groups and utilities
5678;; ----------------------------------------------------------------------------- 5678;; -----------------------------------------------------------------------------
5679 5679
5680(defgroup todos nil 5680(defgroup todo nil
5681 "Create and maintain categorized lists of todo items." 5681 "Create and maintain categorized lists of todo items."
5682 :link '(emacs-commentary-link "todos") 5682 :link '(emacs-commentary-link "todo")
5683 :version "24.4" 5683 :version "24.4"
5684 :group 'calendar) 5684 :group 'calendar)
5685 5685
5686(defgroup todos-edit nil 5686(defgroup todo-edit nil
5687 "User options for adding and editing todo items." 5687 "User options for adding and editing todo items."
5688 :version "24.4" 5688 :version "24.4"
5689 :group 'todos) 5689 :group 'todo)
5690 5690
5691(defgroup todos-categories nil 5691(defgroup todo-categories nil
5692 "User options for Todos Categories mode." 5692 "User options for Todo Categories mode."
5693 :version "24.4" 5693 :version "24.4"
5694 :group 'todos) 5694 :group 'todo)
5695 5695
5696(defgroup todos-filtered nil 5696(defgroup todo-filtered nil
5697 "User options for Todos Filter Items mode." 5697 "User options for Todo Filter Items mode."
5698 :version "24.4" 5698 :version "24.4"
5699 :group 'todos) 5699 :group 'todo)
5700 5700
5701(defgroup todos-display nil 5701(defgroup todo-display nil
5702 "User display options for Todos mode." 5702 "User display options for Todo mode."
5703 :version "24.4" 5703 :version "24.4"
5704 :group 'todos) 5704 :group 'todo)
5705 5705
5706(defgroup todos-faces nil 5706(defgroup todo-faces nil
5707 "Faces for the Todos modes." 5707 "Faces for the Todo modes."
5708 :version "24.4" 5708 :version "24.4"
5709 :group 'todos) 5709 :group 'todo)
5710 5710
5711(defun todos-set-show-current-file (symbol value) 5711(defun todo-set-show-current-file (symbol value)
5712 "The :set function for user option `todos-show-current-file'." 5712 "The :set function for user option `todo-show-current-file'."
5713 (custom-set-default symbol value) 5713 (custom-set-default symbol value)
5714 (if value 5714 (if value
5715 (add-hook 'pre-command-hook 'todos-show-current-file nil t) 5715 (add-hook 'pre-command-hook 'todo-show-current-file nil t)
5716 (remove-hook 'pre-command-hook 'todos-show-current-file t))) 5716 (remove-hook 'pre-command-hook 'todo-show-current-file t)))
5717 5717
5718(defun todos-reset-prefix (symbol value) 5718(defun todo-reset-prefix (symbol value)
5719 "The :set function for `todos-prefix' and `todos-number-prefix'." 5719 "The :set function for `todo-prefix' and `todo-number-prefix'."
5720 (let ((oldvalue (symbol-value symbol)) 5720 (let ((oldvalue (symbol-value symbol))
5721 (files todos-file-buffers)) 5721 (files todo-file-buffers))
5722 (custom-set-default symbol value) 5722 (custom-set-default symbol value)
5723 (when (not (equal value oldvalue)) 5723 (when (not (equal value oldvalue))
5724 (dolist (f files) 5724 (dolist (f files)
5725 (with-current-buffer (find-file-noselect f) 5725 (with-current-buffer (find-file-noselect f)
5726 ;; Activate the new setting in the current category. 5726 ;; Activate the new setting in the current category.
5727 (save-excursion (todos-category-select))))))) 5727 (save-excursion (todo-category-select)))))))
5728 5728
5729(defun todos-reset-nondiary-marker (symbol value) 5729(defun todo-reset-nondiary-marker (symbol value)
5730 "The :set function for user option `todos-nondiary-marker'." 5730 "The :set function for user option `todo-nondiary-marker'."
5731 (let ((oldvalue (symbol-value symbol)) 5731 (let ((oldvalue (symbol-value symbol))
5732 (files (append todos-files todos-archives))) 5732 (files (append todo-files todo-archives)))
5733 (custom-set-default symbol value) 5733 (custom-set-default symbol value)
5734 ;; Need to reset these to get font-locking right. 5734 ;; Need to reset these to get font-locking right.
5735 (setq todos-nondiary-start (nth 0 todos-nondiary-marker) 5735 (setq todo-nondiary-start (nth 0 todo-nondiary-marker)
5736 todos-nondiary-end (nth 1 todos-nondiary-marker) 5736 todo-nondiary-end (nth 1 todo-nondiary-marker)
5737 todos-date-string-start 5737 todo-date-string-start
5738 ;; See comment in defvar of `todos-date-string-start'. 5738 ;; See comment in defvar of `todo-date-string-start'.
5739 (concat "^\\(" (regexp-quote todos-nondiary-start) "\\|" 5739 (concat "^\\(" (regexp-quote todo-nondiary-start) "\\|"
5740 (regexp-quote diary-nonmarking-symbol) "\\)?")) 5740 (regexp-quote diary-nonmarking-symbol) "\\)?"))
5741 (when (not (equal value oldvalue)) 5741 (when (not (equal value oldvalue))
5742 (dolist (f files) 5742 (dolist (f files)
@@ -5746,9 +5746,9 @@ the empty string (i.e., no time string)."
5746 (goto-char (point-min)) 5746 (goto-char (point-min))
5747 (while (not (eobp)) 5747 (while (not (eobp))
5748 (if (re-search-forward 5748 (if (re-search-forward
5749 (concat "^\\(" todos-done-string-start "[^][]+] \\)?" 5749 (concat "^\\(" todo-done-string-start "[^][]+] \\)?"
5750 "\\(?1:" (regexp-quote (car oldvalue)) 5750 "\\(?1:" (regexp-quote (car oldvalue))
5751 "\\)" todos-date-pattern "\\( " 5751 "\\)" todo-date-pattern "\\( "
5752 diary-time-regexp "\\)?\\(?2:" 5752 diary-time-regexp "\\)?\\(?2:"
5753 (regexp-quote (cadr oldvalue)) "\\)") 5753 (regexp-quote (cadr oldvalue)) "\\)")
5754 nil t) 5754 nil t)
@@ -5756,31 +5756,31 @@ the empty string (i.e., no time string)."
5756 (replace-match (nth 0 value) t t nil 1) 5756 (replace-match (nth 0 value) t t nil 1)
5757 (replace-match (nth 1 value) t t nil 2)) 5757 (replace-match (nth 1 value) t t nil 2))
5758 (forward-line))) 5758 (forward-line)))
5759 (todos-category-select))))))) 5759 (todo-category-select)))))))
5760 5760
5761(defun todos-reset-done-separator-string (symbol value) 5761(defun todo-reset-done-separator-string (symbol value)
5762 "The :set function for `todos-done-separator-string'." 5762 "The :set function for `todo-done-separator-string'."
5763 (let ((oldvalue (symbol-value symbol)) 5763 (let ((oldvalue (symbol-value symbol))
5764 (files todos-file-buffers) 5764 (files todo-file-buffers)
5765 (sep todos-done-separator)) 5765 (sep todo-done-separator))
5766 (custom-set-default symbol value) 5766 (custom-set-default symbol value)
5767 (when (not (equal value oldvalue)) 5767 (when (not (equal value oldvalue))
5768 (dolist (f files) 5768 (dolist (f files)
5769 (with-current-buffer (find-file-noselect f) 5769 (with-current-buffer (find-file-noselect f)
5770 (let (buffer-read-only) 5770 (let (buffer-read-only)
5771 (setq todos-done-separator (todos-done-separator)) 5771 (setq todo-done-separator (todo-done-separator))
5772 (when (= 1 (length value)) 5772 (when (= 1 (length value))
5773 (todos-reset-done-separator sep))) 5773 (todo-reset-done-separator sep)))
5774 (todos-category-select)))))) 5774 (todo-category-select))))))
5775 5775
5776(defun todos-reset-done-string (symbol value) 5776(defun todo-reset-done-string (symbol value)
5777 "The :set function for user option `todos-done-string'." 5777 "The :set function for user option `todo-done-string'."
5778 (let ((oldvalue (symbol-value symbol)) 5778 (let ((oldvalue (symbol-value symbol))
5779 (files (append todos-files todos-archives))) 5779 (files (append todo-files todo-archives)))
5780 (custom-set-default symbol value) 5780 (custom-set-default symbol value)
5781 ;; Need to reset this to get font-locking right. 5781 ;; Need to reset this to get font-locking right.
5782 (setq todos-done-string-start 5782 (setq todo-done-string-start
5783 (concat "^\\[" (regexp-quote todos-done-string))) 5783 (concat "^\\[" (regexp-quote todo-done-string)))
5784 (when (not (equal value oldvalue)) 5784 (when (not (equal value oldvalue))
5785 (dolist (f files) 5785 (dolist (f files)
5786 (with-current-buffer (find-file-noselect f) 5786 (with-current-buffer (find-file-noselect f)
@@ -5789,17 +5789,17 @@ the empty string (i.e., no time string)."
5789 (goto-char (point-min)) 5789 (goto-char (point-min))
5790 (while (not (eobp)) 5790 (while (not (eobp))
5791 (if (re-search-forward 5791 (if (re-search-forward
5792 (concat "^" (regexp-quote todos-nondiary-start) 5792 (concat "^" (regexp-quote todo-nondiary-start)
5793 "\\(" (regexp-quote oldvalue) "\\)") 5793 "\\(" (regexp-quote oldvalue) "\\)")
5794 nil t) 5794 nil t)
5795 (replace-match value t t nil 1) 5795 (replace-match value t t nil 1)
5796 (forward-line))) 5796 (forward-line)))
5797 (todos-category-select))))))) 5797 (todo-category-select)))))))
5798 5798
5799(defun todos-reset-comment-string (symbol value) 5799(defun todo-reset-comment-string (symbol value)
5800 "The :set function for user option `todos-comment-string'." 5800 "The :set function for user option `todo-comment-string'."
5801 (let ((oldvalue (symbol-value symbol)) 5801 (let ((oldvalue (symbol-value symbol))
5802 (files (append todos-files todos-archives))) 5802 (files (append todo-files todo-archives)))
5803 (custom-set-default symbol value) 5803 (custom-set-default symbol value)
5804 (when (not (equal value oldvalue)) 5804 (when (not (equal value oldvalue))
5805 (dolist (f files) 5805 (dolist (f files)
@@ -5815,12 +5815,12 @@ the empty string (i.e., no time string)."
5815 nil t) 5815 nil t)
5816 (replace-match value t t nil 1) 5816 (replace-match value t t nil 1)
5817 (forward-line))) 5817 (forward-line)))
5818 (todos-category-select)))))))) 5818 (todo-category-select))))))))
5819 5819
5820(defun todos-reset-highlight-item (symbol value) 5820(defun todo-reset-highlight-item (symbol value)
5821 "The :set function for `todos-toggle-item-highlighting'." 5821 "The :set function for `todo-toggle-item-highlighting'."
5822 (let ((oldvalue (symbol-value symbol)) 5822 (let ((oldvalue (symbol-value symbol))
5823 (files (append todos-files todos-archives))) 5823 (files (append todo-files todo-archives)))
5824 (custom-set-default symbol value) 5824 (custom-set-default symbol value)
5825 (when (not (equal value oldvalue)) 5825 (when (not (equal value oldvalue))
5826 (dolist (f files) 5826 (dolist (f files)
@@ -5832,78 +5832,78 @@ the empty string (i.e., no time string)."
5832 (hl-line-mode 1) 5832 (hl-line-mode 1)
5833 (hl-line-mode -1))))))))) 5833 (hl-line-mode -1)))))))))
5834 5834
5835(defun todos-reevaluate-filelist-defcustoms () 5835(defun todo-reevaluate-filelist-defcustoms ()
5836 "Reevaluate defcustoms that provide choice list of Todos files." 5836 "Reevaluate defcustoms that provide choice list of Todo files."
5837 (custom-set-default 'todos-default-todos-file 5837 (custom-set-default 'todo-default-todo-file
5838 (symbol-value 'todos-default-todos-file)) 5838 (symbol-value 'todo-default-todo-file))
5839 (todos-reevaluate-default-file-defcustom) 5839 (todo-reevaluate-default-file-defcustom)
5840 (custom-set-default 'todos-filter-files (symbol-value 'todos-filter-files)) 5840 (custom-set-default 'todo-filter-files (symbol-value 'todo-filter-files))
5841 (todos-reevaluate-filter-files-defcustom) 5841 (todo-reevaluate-filter-files-defcustom)
5842 (custom-set-default 'todos-category-completions-files 5842 (custom-set-default 'todo-category-completions-files
5843 (symbol-value 'todos-category-completions-files)) 5843 (symbol-value 'todo-category-completions-files))
5844 (todos-reevaluate-category-completions-files-defcustom)) 5844 (todo-reevaluate-category-completions-files-defcustom))
5845 5845
5846(defun todos-reevaluate-default-file-defcustom () 5846(defun todo-reevaluate-default-file-defcustom ()
5847 "Reevaluate defcustom of `todos-default-todos-file'. 5847 "Reevaluate defcustom of `todo-default-todo-file'.
5848Called after adding or deleting a Todos file." 5848Called after adding or deleting a Todo file."
5849 (eval (defcustom todos-default-todos-file (car (funcall todos-files-function)) 5849 (eval (defcustom todo-default-todo-file (car (funcall todo-files-function))
5850 "Todos file visited by first session invocation of `todos-show'." 5850 "Todo file visited by first session invocation of `todo-show'."
5851 :type `(radio ,@(mapcar (lambda (f) (list 'const f)) 5851 :type `(radio ,@(mapcar (lambda (f) (list 'const f))
5852 (mapcar 'todos-short-file-name 5852 (mapcar 'todo-short-file-name
5853 (funcall todos-files-function)))) 5853 (funcall todo-files-function))))
5854 :group 'todos))) 5854 :group 'todo)))
5855 5855
5856(defun todos-reevaluate-category-completions-files-defcustom () 5856(defun todo-reevaluate-category-completions-files-defcustom ()
5857 "Reevaluate defcustom of `todos-category-completions-files'. 5857 "Reevaluate defcustom of `todo-category-completions-files'.
5858Called after adding or deleting a Todos file." 5858Called after adding or deleting a Todo file."
5859 (eval (defcustom todos-category-completions-files nil 5859 (eval (defcustom todo-category-completions-files nil
5860 "List of files for building `todos-read-category' completions." 5860 "List of files for building `todo-read-category' completions."
5861 :type `(set ,@(mapcar (lambda (f) (list 'const f)) 5861 :type `(set ,@(mapcar (lambda (f) (list 'const f))
5862 (mapcar 'todos-short-file-name 5862 (mapcar 'todo-short-file-name
5863 (funcall todos-files-function)))) 5863 (funcall todo-files-function))))
5864 :group 'todos))) 5864 :group 'todo)))
5865 5865
5866(defun todos-reevaluate-filter-files-defcustom () 5866(defun todo-reevaluate-filter-files-defcustom ()
5867 "Reevaluate defcustom of `todos-filter-files'. 5867 "Reevaluate defcustom of `todo-filter-files'.
5868Called after adding or deleting a Todos file." 5868Called after adding or deleting a Todo file."
5869 (eval (defcustom todos-filter-files nil 5869 (eval (defcustom todo-filter-files nil
5870 "List of files for multifile item filtering." 5870 "List of files for multifile item filtering."
5871 :type `(set ,@(mapcar (lambda (f) (list 'const f)) 5871 :type `(set ,@(mapcar (lambda (f) (list 'const f))
5872 (mapcar 'todos-short-file-name 5872 (mapcar 'todo-short-file-name
5873 (funcall todos-files-function)))) 5873 (funcall todo-files-function))))
5874 :group 'todos))) 5874 :group 'todo)))
5875 5875
5876;; ----------------------------------------------------------------------------- 5876;; -----------------------------------------------------------------------------
5877;;; Font locking 5877;;; Font locking
5878;; ----------------------------------------------------------------------------- 5878;; -----------------------------------------------------------------------------
5879 5879
5880(defun todos-nondiary-marker-matcher (lim) 5880(defun todo-nondiary-marker-matcher (lim)
5881 "Search for Todos nondiary markers within LIM for font-locking." 5881 "Search for Todo nondiary markers within LIM for font-locking."
5882 (re-search-forward (concat "^\\(?1:" (regexp-quote todos-nondiary-start) "\\)" 5882 (re-search-forward (concat "^\\(?1:" (regexp-quote todo-nondiary-start) "\\)"
5883 todos-date-pattern "\\(?: " diary-time-regexp 5883 todo-date-pattern "\\(?: " diary-time-regexp
5884 "\\)?\\(?2:" (regexp-quote todos-nondiary-end) "\\)") 5884 "\\)?\\(?2:" (regexp-quote todo-nondiary-end) "\\)")
5885 lim t)) 5885 lim t))
5886 5886
5887(defun todos-diary-nonmarking-matcher (lim) 5887(defun todo-diary-nonmarking-matcher (lim)
5888 "Search for diary nonmarking symbol within LIM for font-locking." 5888 "Search for diary nonmarking symbol within LIM for font-locking."
5889 (re-search-forward (concat "^\\(?1:" (regexp-quote diary-nonmarking-symbol) 5889 (re-search-forward (concat "^\\(?1:" (regexp-quote diary-nonmarking-symbol)
5890 "\\)" todos-date-pattern) lim t)) 5890 "\\)" todo-date-pattern) lim t))
5891 5891
5892(defun todos-date-string-matcher (lim) 5892(defun todo-date-string-matcher (lim)
5893 "Search for Todos date string within LIM for font-locking." 5893 "Search for Todo date string within LIM for font-locking."
5894 (re-search-forward 5894 (re-search-forward
5895 (concat todos-date-string-start "\\(?1:" todos-date-pattern "\\)") lim t)) 5895 (concat todo-date-string-start "\\(?1:" todo-date-pattern "\\)") lim t))
5896 5896
5897(defun todos-time-string-matcher (lim) 5897(defun todo-time-string-matcher (lim)
5898 "Search for Todos time string within LIM for font-locking." 5898 "Search for Todo time string within LIM for font-locking."
5899 (re-search-forward (concat todos-date-string-start todos-date-pattern 5899 (re-search-forward (concat todo-date-string-start todo-date-pattern
5900 " \\(?1:" diary-time-regexp "\\)") lim t)) 5900 " \\(?1:" diary-time-regexp "\\)") lim t))
5901 5901
5902(defun todos-diary-expired-matcher (lim) 5902(defun todo-diary-expired-matcher (lim)
5903 "Search for expired diary item date within LIM for font-locking." 5903 "Search for expired diary item date within LIM for font-locking."
5904 (when (re-search-forward (concat "^\\(?:" 5904 (when (re-search-forward (concat "^\\(?:"
5905 (regexp-quote diary-nonmarking-symbol) 5905 (regexp-quote diary-nonmarking-symbol)
5906 "\\)?\\(?1:" todos-date-pattern "\\) \\(?2:" 5906 "\\)?\\(?1:" todo-date-pattern "\\) \\(?2:"
5907 diary-time-regexp "\\)?") lim t) 5907 diary-time-regexp "\\)?") lim t)
5908 (let* ((date (match-string-no-properties 1)) 5908 (let* ((date (match-string-no-properties 1))
5909 (time (match-string-no-properties 2)) 5909 (time (match-string-no-properties 2))
@@ -5911,25 +5911,25 @@ Called after adding or deleting a Todos file."
5911 (date-time (concat date " " (or time "00:00")))) 5911 (date-time (concat date " " (or time "00:00"))))
5912 (or (and (not (string-match ".+day\\|\\*" date)) 5912 (or (and (not (string-match ".+day\\|\\*" date))
5913 (< (days-between date-time (current-time-string)) 0)) 5913 (< (days-between date-time (current-time-string)) 0))
5914 (todos-diary-expired-matcher lim))))) 5914 (todo-diary-expired-matcher lim)))))
5915 5915
5916(defun todos-done-string-matcher (lim) 5916(defun todo-done-string-matcher (lim)
5917 "Search for Todos done header within LIM for font-locking." 5917 "Search for Todo done header within LIM for font-locking."
5918 (re-search-forward (concat todos-done-string-start 5918 (re-search-forward (concat todo-done-string-start
5919 "[^][]+]") 5919 "[^][]+]")
5920 lim t)) 5920 lim t))
5921 5921
5922(defun todos-comment-string-matcher (lim) 5922(defun todo-comment-string-matcher (lim)
5923 "Search for Todos done comment within LIM for font-locking." 5923 "Search for Todo done comment within LIM for font-locking."
5924 (re-search-forward (concat "\\[\\(?1:" todos-comment-string "\\):") 5924 (re-search-forward (concat "\\[\\(?1:" todo-comment-string "\\):")
5925 lim t)) 5925 lim t))
5926 5926
5927(defun todos-category-string-matcher-1 (lim) 5927(defun todo-category-string-matcher-1 (lim)
5928 "Search for Todos category name within LIM for font-locking. 5928 "Search for Todo category name within LIM for font-locking.
5929This is for fontifying category and file names appearing in Todos 5929This is for fontifying category and file names appearing in Todo
5930Filtered Items mode following done items." 5930Filtered Items mode following done items."
5931 (if (eq major-mode 'todos-filtered-items-mode) 5931 (if (eq major-mode 'todo-filtered-items-mode)
5932 (re-search-forward (concat todos-done-string-start todos-date-pattern 5932 (re-search-forward (concat todo-done-string-start todo-date-pattern
5933 "\\(?: " diary-time-regexp 5933 "\\(?: " diary-time-regexp
5934 ;; Use non-greedy operator to prevent 5934 ;; Use non-greedy operator to prevent
5935 ;; capturing possible following non-diary 5935 ;; capturing possible following non-diary
@@ -5937,432 +5937,432 @@ Filtered Items mode following done items."
5937 "\\)?] \\(?1:\\[.+?\\]\\)") 5937 "\\)?] \\(?1:\\[.+?\\]\\)")
5938 lim t))) 5938 lim t)))
5939 5939
5940(defun todos-category-string-matcher-2 (lim) 5940(defun todo-category-string-matcher-2 (lim)
5941 "Search for Todos category name within LIM for font-locking. 5941 "Search for Todo category name within LIM for font-locking.
5942This is for fontifying category and file names appearing in Todos 5942This is for fontifying category and file names appearing in Todo
5943Filtered Items mode following todo (not done) items." 5943Filtered Items mode following todo (not done) items."
5944 (if (eq major-mode 'todos-filtered-items-mode) 5944 (if (eq major-mode 'todo-filtered-items-mode)
5945 (re-search-forward (concat todos-date-string-start todos-date-pattern 5945 (re-search-forward (concat todo-date-string-start todo-date-pattern
5946 "\\(?: " diary-time-regexp "\\)?\\(?:" 5946 "\\(?: " diary-time-regexp "\\)?\\(?:"
5947 (regexp-quote todos-nondiary-end) 5947 (regexp-quote todo-nondiary-end)
5948 "\\)? \\(?1:\\[.+\\]\\)") 5948 "\\)? \\(?1:\\[.+\\]\\)")
5949 lim t))) 5949 lim t)))
5950 5950
5951(defvar todos-nondiary-face 'todos-nondiary) 5951(defvar todo-nondiary-face 'todo-nondiary)
5952(defvar todos-date-face 'todos-date) 5952(defvar todo-date-face 'todo-date)
5953(defvar todos-time-face 'todos-time) 5953(defvar todo-time-face 'todo-time)
5954(defvar todos-diary-expired-face 'todos-diary-expired) 5954(defvar todo-diary-expired-face 'todo-diary-expired)
5955(defvar todos-done-sep-face 'todos-done-sep) 5955(defvar todo-done-sep-face 'todo-done-sep)
5956(defvar todos-done-face 'todos-done) 5956(defvar todo-done-face 'todo-done)
5957(defvar todos-comment-face 'todos-comment) 5957(defvar todo-comment-face 'todo-comment)
5958(defvar todos-category-string-face 'todos-category-string) 5958(defvar todo-category-string-face 'todo-category-string)
5959(defvar todos-font-lock-keywords 5959(defvar todo-font-lock-keywords
5960 (list 5960 (list
5961 '(todos-nondiary-marker-matcher 1 todos-nondiary-face t) 5961 '(todo-nondiary-marker-matcher 1 todo-nondiary-face t)
5962 '(todos-nondiary-marker-matcher 2 todos-nondiary-face t) 5962 '(todo-nondiary-marker-matcher 2 todo-nondiary-face t)
5963 ;; diary-lib.el uses font-lock-constant-face for diary-nonmarking-symbol. 5963 ;; diary-lib.el uses font-lock-constant-face for diary-nonmarking-symbol.
5964 '(todos-diary-nonmarking-matcher 1 font-lock-constant-face t) 5964 '(todo-diary-nonmarking-matcher 1 font-lock-constant-face t)
5965 '(todos-date-string-matcher 1 todos-date-face t) 5965 '(todo-date-string-matcher 1 todo-date-face t)
5966 '(todos-time-string-matcher 1 todos-time-face t) 5966 '(todo-time-string-matcher 1 todo-time-face t)
5967 '(todos-done-string-matcher 0 todos-done-face t) 5967 '(todo-done-string-matcher 0 todo-done-face t)
5968 '(todos-comment-string-matcher 1 todos-comment-face t) 5968 '(todo-comment-string-matcher 1 todo-comment-face t)
5969 '(todos-category-string-matcher-1 1 todos-category-string-face t t) 5969 '(todo-category-string-matcher-1 1 todo-category-string-face t t)
5970 '(todos-category-string-matcher-2 1 todos-category-string-face t t) 5970 '(todo-category-string-matcher-2 1 todo-category-string-face t t)
5971 '(todos-diary-expired-matcher 1 todos-diary-expired-face t) 5971 '(todo-diary-expired-matcher 1 todo-diary-expired-face t)
5972 '(todos-diary-expired-matcher 2 todos-diary-expired-face t t) 5972 '(todo-diary-expired-matcher 2 todo-diary-expired-face t t)
5973 ) 5973 )
5974 "Font-locking for Todos modes.") 5974 "Font-locking for Todo modes.")
5975 5975
5976;; ----------------------------------------------------------------------------- 5976;; -----------------------------------------------------------------------------
5977;;; Key binding 5977;;; Key binding
5978;; ----------------------------------------------------------------------------- 5978;; -----------------------------------------------------------------------------
5979 5979
5980(defvar todos-insertion-map 5980(defvar todo-insertion-map
5981 (let ((map (make-keymap))) 5981 (let ((map (make-keymap)))
5982 (todos-insertion-key-bindings map) 5982 (todo-insertion-key-bindings map)
5983 (define-key map "p" 'todos-copy-item) 5983 (define-key map "p" 'todo-copy-item)
5984 map) 5984 map)
5985 "Keymap for Todos mode item insertion commands.") 5985 "Keymap for Todo mode item insertion commands.")
5986 5986
5987(defvar todos-key-bindings-t 5987(defvar todo-key-bindings-t
5988 `( 5988 `(
5989 ("Af" todos-find-archive) 5989 ("Af" todo-find-archive)
5990 ("Ac" todos-choose-archive) 5990 ("Ac" todo-choose-archive)
5991 ("Ad" todos-archive-done-item) 5991 ("Ad" todo-archive-done-item)
5992 ("Cv" todos-toggle-view-done-items) 5992 ("Cv" todo-toggle-view-done-items)
5993 ("v" todos-toggle-view-done-items) 5993 ("v" todo-toggle-view-done-items)
5994 ("Ca" todos-add-category) 5994 ("Ca" todo-add-category)
5995 ("Cr" todos-rename-category) 5995 ("Cr" todo-rename-category)
5996 ("Cg" todos-merge-category) 5996 ("Cg" todo-merge-category)
5997 ("Cm" todos-move-category) 5997 ("Cm" todo-move-category)
5998 ("Ck" todos-delete-category) 5998 ("Ck" todo-delete-category)
5999 ("Cts" todos-set-top-priorities-in-category) 5999 ("Cts" todo-set-top-priorities-in-category)
6000 ("Cey" todos-edit-category-diary-inclusion) 6000 ("Cey" todo-edit-category-diary-inclusion)
6001 ("Cek" todos-edit-category-diary-nonmarking) 6001 ("Cek" todo-edit-category-diary-nonmarking)
6002 ("Fa" todos-add-file) 6002 ("Fa" todo-add-file)
6003 ("Ff" todos-find-filtered-items-file) 6003 ("Ff" todo-find-filtered-items-file)
6004 ("FV" todos-toggle-view-done-only) 6004 ("FV" todo-toggle-view-done-only)
6005 ("V" todos-toggle-view-done-only) 6005 ("V" todo-toggle-view-done-only)
6006 ("Ftt" todos-filter-top-priorities) 6006 ("Ftt" todo-filter-top-priorities)
6007 ("Ftm" todos-filter-top-priorities-multifile) 6007 ("Ftm" todo-filter-top-priorities-multifile)
6008 ("Fts" todos-set-top-priorities-in-file) 6008 ("Fts" todo-set-top-priorities-in-file)
6009 ("Fyy" todos-filter-diary-items) 6009 ("Fyy" todo-filter-diary-items)
6010 ("Fym" todos-filter-diary-items-multifile) 6010 ("Fym" todo-filter-diary-items-multifile)
6011 ("Frr" todos-filter-regexp-items) 6011 ("Frr" todo-filter-regexp-items)
6012 ("Frm" todos-filter-regexp-items-multifile) 6012 ("Frm" todo-filter-regexp-items-multifile)
6013 ("ee" todos-edit-item) 6013 ("ee" todo-edit-item)
6014 ("em" todos-edit-multiline-item) 6014 ("em" todo-edit-multiline-item)
6015 ("edt" todos-edit-item-header) 6015 ("edt" todo-edit-item-header)
6016 ("edc" todos-edit-item-date-from-calendar) 6016 ("edc" todo-edit-item-date-from-calendar)
6017 ("eda" todos-edit-item-date-to-today) 6017 ("eda" todo-edit-item-date-to-today)
6018 ("edn" todos-edit-item-date-day-name) 6018 ("edn" todo-edit-item-date-day-name)
6019 ("edy" todos-edit-item-date-year) 6019 ("edy" todo-edit-item-date-year)
6020 ("edm" todos-edit-item-date-month) 6020 ("edm" todo-edit-item-date-month)
6021 ("edd" todos-edit-item-date-day) 6021 ("edd" todo-edit-item-date-day)
6022 ("et" todos-edit-item-time) 6022 ("et" todo-edit-item-time)
6023 ("eyy" todos-edit-item-diary-inclusion) 6023 ("eyy" todo-edit-item-diary-inclusion)
6024 ("eyk" todos-edit-item-diary-nonmarking) 6024 ("eyk" todo-edit-item-diary-nonmarking)
6025 ("ec" todos-edit-done-item-comment) 6025 ("ec" todo-edit-done-item-comment)
6026 ("d" todos-item-done) 6026 ("d" todo-item-done)
6027 ("i" ,todos-insertion-map) 6027 ("i" ,todo-insertion-map)
6028 ("k" todos-delete-item) 6028 ("k" todo-delete-item)
6029 ("m" todos-move-item) 6029 ("m" todo-move-item)
6030 ("u" todos-item-undone) 6030 ("u" todo-item-undone)
6031 ([remap newline] newline-and-indent) 6031 ([remap newline] newline-and-indent)
6032 ) 6032 )
6033 "List of key bindings for Todos mode only.") 6033 "List of key bindings for Todo mode only.")
6034 6034
6035(defvar todos-key-bindings-t+a+f 6035(defvar todo-key-bindings-t+a+f
6036 `( 6036 `(
6037 ("C*" todos-mark-category) 6037 ("C*" todo-mark-category)
6038 ("Cu" todos-unmark-category) 6038 ("Cu" todo-unmark-category)
6039 ("Fh" todos-toggle-item-header) 6039 ("Fh" todo-toggle-item-header)
6040 ("h" todos-toggle-item-header) 6040 ("h" todo-toggle-item-header)
6041 ("Fe" todos-edit-file) 6041 ("Fe" todo-edit-file)
6042 ("FH" todos-toggle-item-highlighting) 6042 ("FH" todo-toggle-item-highlighting)
6043 ("H" todos-toggle-item-highlighting) 6043 ("H" todo-toggle-item-highlighting)
6044 ("FN" todos-toggle-prefix-numbers) 6044 ("FN" todo-toggle-prefix-numbers)
6045 ("N" todos-toggle-prefix-numbers) 6045 ("N" todo-toggle-prefix-numbers)
6046 ("PB" todos-print-buffer) 6046 ("PB" todo-print-buffer)
6047 ("PF" todos-print-buffer-to-file) 6047 ("PF" todo-print-buffer-to-file)
6048 ("b" todos-backward-category) 6048 ("b" todo-backward-category)
6049 ("d" todos-item-done) 6049 ("d" todo-item-done)
6050 ("f" todos-forward-category) 6050 ("f" todo-forward-category)
6051 ("j" todos-jump-to-category) 6051 ("j" todo-jump-to-category)
6052 ("n" todos-next-item) 6052 ("n" todo-next-item)
6053 ("p" todos-previous-item) 6053 ("p" todo-previous-item)
6054 ("q" todos-quit) 6054 ("q" todo-quit)
6055 ("s" todos-save) 6055 ("s" todo-save)
6056 ("t" todos-show) 6056 ("t" todo-show)
6057 ) 6057 )
6058 "List of key bindings for Todos, Archive, and Filtered Items modes.") 6058 "List of key bindings for Todo, Archive, and Filtered Items modes.")
6059 6059
6060(defvar todos-key-bindings-t+a 6060(defvar todo-key-bindings-t+a
6061 `( 6061 `(
6062 ("Fc" todos-show-categories-table) 6062 ("Fc" todo-show-categories-table)
6063 ("S" todos-search) 6063 ("S" todo-search)
6064 ("X" todos-clear-matches) 6064 ("X" todo-clear-matches)
6065 ("*" todos-toggle-mark-item) 6065 ("*" todo-toggle-mark-item)
6066 ) 6066 )
6067 "List of key bindings for Todos and Todos Archive modes.") 6067 "List of key bindings for Todo and Todo Archive modes.")
6068 6068
6069(defvar todos-key-bindings-t+f 6069(defvar todo-key-bindings-t+f
6070 `( 6070 `(
6071 ("l" todos-lower-item-priority) 6071 ("l" todo-lower-item-priority)
6072 ("r" todos-raise-item-priority) 6072 ("r" todo-raise-item-priority)
6073 ("#" todos-set-item-priority) 6073 ("#" todo-set-item-priority)
6074 ) 6074 )
6075 "List of key bindings for Todos and Todos Filtered Items modes.") 6075 "List of key bindings for Todo and Todo Filtered Items modes.")
6076 6076
6077(defvar todos-mode-map 6077(defvar todo-mode-map
6078 (let ((map (make-keymap))) 6078 (let ((map (make-keymap)))
6079 ;; Don't suppress digit keys, so they can supply prefix arguments. 6079 ;; Don't suppress digit keys, so they can supply prefix arguments.
6080 (suppress-keymap map) 6080 (suppress-keymap map)
6081 (dolist (kb todos-key-bindings-t) 6081 (dolist (kb todo-key-bindings-t)
6082 (define-key map (nth 0 kb) (nth 1 kb))) 6082 (define-key map (nth 0 kb) (nth 1 kb)))
6083 (dolist (kb todos-key-bindings-t+a+f) 6083 (dolist (kb todo-key-bindings-t+a+f)
6084 (define-key map (nth 0 kb) (nth 1 kb))) 6084 (define-key map (nth 0 kb) (nth 1 kb)))
6085 (dolist (kb todos-key-bindings-t+a) 6085 (dolist (kb todo-key-bindings-t+a)
6086 (define-key map (nth 0 kb) (nth 1 kb))) 6086 (define-key map (nth 0 kb) (nth 1 kb)))
6087 (dolist (kb todos-key-bindings-t+f) 6087 (dolist (kb todo-key-bindings-t+f)
6088 (define-key map (nth 0 kb) (nth 1 kb))) 6088 (define-key map (nth 0 kb) (nth 1 kb)))
6089 map) 6089 map)
6090 "Todos mode keymap.") 6090 "Todo mode keymap.")
6091 6091
6092(defvar todos-archive-mode-map 6092(defvar todo-archive-mode-map
6093 (let ((map (make-sparse-keymap))) 6093 (let ((map (make-sparse-keymap)))
6094 (suppress-keymap map) 6094 (suppress-keymap map)
6095 (dolist (kb todos-key-bindings-t+a+f) 6095 (dolist (kb todo-key-bindings-t+a+f)
6096 (define-key map (nth 0 kb) (nth 1 kb))) 6096 (define-key map (nth 0 kb) (nth 1 kb)))
6097 (dolist (kb todos-key-bindings-t+a) 6097 (dolist (kb todo-key-bindings-t+a)
6098 (define-key map (nth 0 kb) (nth 1 kb))) 6098 (define-key map (nth 0 kb) (nth 1 kb)))
6099 (define-key map "a" 'todos-jump-to-archive-category) 6099 (define-key map "a" 'todo-jump-to-archive-category)
6100 (define-key map "u" 'todos-unarchive-items) 6100 (define-key map "u" 'todo-unarchive-items)
6101 map) 6101 map)
6102 "Todos Archive mode keymap.") 6102 "Todo Archive mode keymap.")
6103 6103
6104(defvar todos-edit-mode-map 6104(defvar todo-edit-mode-map
6105 (let ((map (make-sparse-keymap))) 6105 (let ((map (make-sparse-keymap)))
6106 (define-key map "\C-x\C-q" 'todos-edit-quit) 6106 (define-key map "\C-x\C-q" 'todo-edit-quit)
6107 (define-key map [remap newline] 'newline-and-indent) 6107 (define-key map [remap newline] 'newline-and-indent)
6108 map) 6108 map)
6109 "Todos Edit mode keymap.") 6109 "Todo Edit mode keymap.")
6110 6110
6111(defvar todos-categories-mode-map 6111(defvar todo-categories-mode-map
6112 (let ((map (make-sparse-keymap))) 6112 (let ((map (make-sparse-keymap)))
6113 (suppress-keymap map) 6113 (suppress-keymap map)
6114 (define-key map "c" 'todos-sort-categories-alphabetically-or-numerically) 6114 (define-key map "c" 'todo-sort-categories-alphabetically-or-numerically)
6115 (define-key map "t" 'todos-sort-categories-by-todo) 6115 (define-key map "t" 'todo-sort-categories-by-todo)
6116 (define-key map "y" 'todos-sort-categories-by-diary) 6116 (define-key map "y" 'todo-sort-categories-by-diary)
6117 (define-key map "d" 'todos-sort-categories-by-done) 6117 (define-key map "d" 'todo-sort-categories-by-done)
6118 (define-key map "a" 'todos-sort-categories-by-archived) 6118 (define-key map "a" 'todo-sort-categories-by-archived)
6119 (define-key map "#" 'todos-set-category-number) 6119 (define-key map "#" 'todo-set-category-number)
6120 (define-key map "l" 'todos-lower-category) 6120 (define-key map "l" 'todo-lower-category)
6121 (define-key map "r" 'todos-raise-category) 6121 (define-key map "r" 'todo-raise-category)
6122 (define-key map "n" 'todos-next-button) 6122 (define-key map "n" 'todo-next-button)
6123 (define-key map "p" 'todos-previous-button) 6123 (define-key map "p" 'todo-previous-button)
6124 (define-key map [tab] 'todos-next-button) 6124 (define-key map [tab] 'todo-next-button)
6125 (define-key map [backtab] 'todos-previous-button) 6125 (define-key map [backtab] 'todo-previous-button)
6126 (define-key map "q" 'todos-quit) 6126 (define-key map "q" 'todo-quit)
6127 map) 6127 map)
6128 "Todos Categories mode keymap.") 6128 "Todo Categories mode keymap.")
6129 6129
6130(defvar todos-filtered-items-mode-map 6130(defvar todo-filtered-items-mode-map
6131 (let ((map (make-sparse-keymap))) 6131 (let ((map (make-sparse-keymap)))
6132 (suppress-keymap map) 6132 (suppress-keymap map)
6133 (dolist (kb todos-key-bindings-t+a+f) 6133 (dolist (kb todo-key-bindings-t+a+f)
6134 (define-key map (nth 0 kb) (nth 1 kb))) 6134 (define-key map (nth 0 kb) (nth 1 kb)))
6135 (dolist (kb todos-key-bindings-t+f) 6135 (dolist (kb todo-key-bindings-t+f)
6136 (define-key map (nth 0 kb) (nth 1 kb))) 6136 (define-key map (nth 0 kb) (nth 1 kb)))
6137 (define-key map "g" 'todos-go-to-source-item) 6137 (define-key map "g" 'todo-go-to-source-item)
6138 (define-key map [remap newline] 'todos-go-to-source-item) 6138 (define-key map [remap newline] 'todo-go-to-source-item)
6139 map) 6139 map)
6140 "Todos Filtered Items mode keymap.") 6140 "Todo Filtered Items mode keymap.")
6141 6141
6142;; FIXME: Is it worth having a menu and if so, which commands? 6142;; FIXME: Is it worth having a menu and if so, which commands?
6143;; (easy-menu-define 6143;; (easy-menu-define
6144;; todos-menu todos-mode-map "Todos Menu" 6144;; todo-menu todo-mode-map "Todo Menu"
6145;; '("Todos" 6145;; '("Todo"
6146;; ("Navigation" 6146;; ("Navigation"
6147;; ["Next Item" todos-forward-item t] 6147;; ["Next Item" todo-forward-item t]
6148;; ["Previous Item" todos-backward-item t] 6148;; ["Previous Item" todo-backward-item t]
6149;; "---" 6149;; "---"
6150;; ["Next Category" todos-forward-category t] 6150;; ["Next Category" todo-forward-category t]
6151;; ["Previous Category" todos-backward-category t] 6151;; ["Previous Category" todo-backward-category t]
6152;; ["Jump to Category" todos-jump-to-category t] 6152;; ["Jump to Category" todo-jump-to-category t]
6153;; "---" 6153;; "---"
6154;; ["Search Todos File" todos-search t] 6154;; ["Search Todo File" todo-search t]
6155;; ["Clear Highlighting on Search Matches" todos-category-done t]) 6155;; ["Clear Highlighting on Search Matches" todo-category-done t])
6156;; ("Display" 6156;; ("Display"
6157;; ["List Current Categories" todos-show-categories-table t] 6157;; ["List Current Categories" todo-show-categories-table t]
6158;; ;; ["List Categories Alphabetically" todos-display-categories-alphabetically t] 6158;; ;; ["List Categories Alphabetically" todo-display-categories-alphabetically t]
6159;; ["Turn Item Highlighting on/off" todos-toggle-item-highlighting t] 6159;; ["Turn Item Highlighting on/off" todo-toggle-item-highlighting t]
6160;; ["Turn Item Numbering on/off" todos-toggle-prefix-numbers t] 6160;; ["Turn Item Numbering on/off" todo-toggle-prefix-numbers t]
6161;; ["Turn Item Time Stamp on/off" todos-toggle-item-header t] 6161;; ["Turn Item Time Stamp on/off" todo-toggle-item-header t]
6162;; ["View/Hide Done Items" todos-toggle-view-done-items t] 6162;; ["View/Hide Done Items" todo-toggle-view-done-items t]
6163;; "---" 6163;; "---"
6164;; ["View Diary Items" todos-filter-diary-items t] 6164;; ["View Diary Items" todo-filter-diary-items t]
6165;; ["View Top Priority Items" todos-filter-top-priorities t] 6165;; ["View Top Priority Items" todo-filter-top-priorities t]
6166;; ["View Multifile Top Priority Items" todos-filter-top-priorities-multifile t] 6166;; ["View Multifile Top Priority Items" todo-filter-top-priorities-multifile t]
6167;; "---" 6167;; "---"
6168;; ["Print Category" todos-print-buffer t]) 6168;; ["Print Category" todo-print-buffer t])
6169;; ("Editing" 6169;; ("Editing"
6170;; ["Insert New Item" todos-insert-item t] 6170;; ["Insert New Item" todo-insert-item t]
6171;; ["Insert Item Here" todos-insert-item-here t] 6171;; ["Insert Item Here" todo-insert-item-here t]
6172;; ("More Insertion Commands") 6172;; ("More Insertion Commands")
6173;; ["Edit Item" todos-edit-item t] 6173;; ["Edit Item" todo-edit-item t]
6174;; ["Edit Multiline Item" todos-edit-multiline-item t] 6174;; ["Edit Multiline Item" todo-edit-multiline-item t]
6175;; ["Edit Item Header" todos-edit-item-header t] 6175;; ["Edit Item Header" todo-edit-item-header t]
6176;; ["Edit Item Date" todos-edit-item-date t] 6176;; ["Edit Item Date" todo-edit-item-date t]
6177;; ["Edit Item Time" todos-edit-item-time t] 6177;; ["Edit Item Time" todo-edit-item-time t]
6178;; "---" 6178;; "---"
6179;; ["Lower Item Priority" todos-lower-item-priority t] 6179;; ["Lower Item Priority" todo-lower-item-priority t]
6180;; ["Raise Item Priority" todos-raise-item-priority t] 6180;; ["Raise Item Priority" todo-raise-item-priority t]
6181;; ["Set Item Priority" todos-set-item-priority t] 6181;; ["Set Item Priority" todo-set-item-priority t]
6182;; ["Move (Recategorize) Item" todos-move-item t] 6182;; ["Move (Recategorize) Item" todo-move-item t]
6183;; ["Delete Item" todos-delete-item t] 6183;; ["Delete Item" todo-delete-item t]
6184;; ["Undo Done Item" todos-item-undone t] 6184;; ["Undo Done Item" todo-item-undone t]
6185;; ["Mark/Unmark Item for Diary" todos-toggle-item-diary-inclusion t] 6185;; ["Mark/Unmark Item for Diary" todo-toggle-item-diary-inclusion t]
6186;; ["Mark/Unmark Items for Diary" todos-edit-item-diary-inclusion t] 6186;; ["Mark/Unmark Items for Diary" todo-edit-item-diary-inclusion t]
6187;; ["Mark & Hide Done Item" todos-item-done t] 6187;; ["Mark & Hide Done Item" todo-item-done t]
6188;; ["Archive Done Items" todos-archive-category-done-items t] 6188;; ["Archive Done Items" todo-archive-category-done-items t]
6189;; "---" 6189;; "---"
6190;; ["Add New Todos File" todos-add-file t] 6190;; ["Add New Todo File" todo-add-file t]
6191;; ["Add New Category" todos-add-category t] 6191;; ["Add New Category" todo-add-category t]
6192;; ["Delete Current Category" todos-delete-category t] 6192;; ["Delete Current Category" todo-delete-category t]
6193;; ["Rename Current Category" todos-rename-category t] 6193;; ["Rename Current Category" todo-rename-category t]
6194;; "---" 6194;; "---"
6195;; ["Save Todos File" todos-save t] 6195;; ["Save Todo File" todo-save t]
6196;; ) 6196;; )
6197;; "---" 6197;; "---"
6198;; ["Quit" todos-quit t] 6198;; ["Quit" todo-quit t]
6199;; )) 6199;; ))
6200 6200
6201;; ----------------------------------------------------------------------------- 6201;; -----------------------------------------------------------------------------
6202;;; Hook functions and mode definitions 6202;;; Hook functions and mode definitions
6203;; ----------------------------------------------------------------------------- 6203;; -----------------------------------------------------------------------------
6204 6204
6205(defun todos-show-current-file () 6205(defun todo-show-current-file ()
6206 "Visit current instead of default Todos file with `todos-show'. 6206 "Visit current instead of default Todo file with `todo-show'.
6207This function is added to `pre-command-hook' when user option 6207This function is added to `pre-command-hook' when user option
6208`todos-show-current-file' is set to non-nil." 6208`todo-show-current-file' is set to non-nil."
6209 (setq todos-global-current-todos-file todos-current-todos-file)) 6209 (setq todo-global-current-todo-file todo-current-todo-file))
6210 6210
6211(defun todos-display-as-todos-file () 6211(defun todo-display-as-todo-file ()
6212 "Show Todos files correctly when visited from outside of Todos mode." 6212 "Show Todo files correctly when visited from outside of Todo mode."
6213 (and (member this-command todos-visit-files-commands) 6213 (and (member this-command todo-visit-files-commands)
6214 (= (- (point-max) (point-min)) (buffer-size)) 6214 (= (- (point-max) (point-min)) (buffer-size))
6215 (member major-mode '(todos-mode todos-archive-mode)) 6215 (member major-mode '(todo-mode todo-archive-mode))
6216 (todos-category-select))) 6216 (todo-category-select)))
6217 6217
6218(defun todos-add-to-buffer-list () 6218(defun todo-add-to-buffer-list ()
6219 "Add name of just visited Todos file to `todos-file-buffers'. 6219 "Add name of just visited Todo file to `todo-file-buffers'.
6220This function is added to `find-file-hook' in Todos mode." 6220This function is added to `find-file-hook' in Todo mode."
6221 (let ((filename (file-truename (buffer-file-name)))) 6221 (let ((filename (file-truename (buffer-file-name))))
6222 (when (member filename todos-files) 6222 (when (member filename todo-files)
6223 (add-to-list 'todos-file-buffers filename)))) 6223 (add-to-list 'todo-file-buffers filename))))
6224 6224
6225(defun todos-update-buffer-list () 6225(defun todo-update-buffer-list ()
6226 "Make current Todos mode buffer file car of `todos-file-buffers'. 6226 "Make current Todo mode buffer file car of `todo-file-buffers'.
6227This function is added to `post-command-hook' in Todos mode." 6227This function is added to `post-command-hook' in Todo mode."
6228 (let ((filename (file-truename (buffer-file-name)))) 6228 (let ((filename (file-truename (buffer-file-name))))
6229 (unless (eq (car todos-file-buffers) filename) 6229 (unless (eq (car todo-file-buffers) filename)
6230 (setq todos-file-buffers 6230 (setq todo-file-buffers
6231 (cons filename (delete filename todos-file-buffers)))))) 6231 (cons filename (delete filename todo-file-buffers))))))
6232 6232
6233(defun todos-reset-global-current-todos-file () 6233(defun todo-reset-global-current-todo-file ()
6234 "Update the value of `todos-global-current-todos-file'. 6234 "Update the value of `todo-global-current-todo-file'.
6235This becomes the latest existing Todos file or, if there is none, 6235This becomes the latest existing Todo file or, if there is none,
6236the value of `todos-default-todos-file'. 6236the value of `todo-default-todo-file'.
6237This function is added to `kill-buffer-hook' in Todos mode." 6237This function is added to `kill-buffer-hook' in Todo mode."
6238 (let ((filename (file-truename (buffer-file-name)))) 6238 (let ((filename (file-truename (buffer-file-name))))
6239 (setq todos-file-buffers (delete filename todos-file-buffers)) 6239 (setq todo-file-buffers (delete filename todo-file-buffers))
6240 (setq todos-global-current-todos-file 6240 (setq todo-global-current-todo-file
6241 (or (car todos-file-buffers) 6241 (or (car todo-file-buffers)
6242 (todos-absolute-file-name todos-default-todos-file))))) 6242 (todo-absolute-file-name todo-default-todo-file)))))
6243 6243
6244(defun todos-reset-and-enable-done-separator () 6244(defun todo-reset-and-enable-done-separator ()
6245 "Show resized done items separator overlay after window change. 6245 "Show resized done items separator overlay after window change.
6246Added to `window-configuration-change-hook' in `todos-mode'." 6246Added to `window-configuration-change-hook' in `todo-mode'."
6247 (when (= 1 (length todos-done-separator-string)) 6247 (when (= 1 (length todo-done-separator-string))
6248 (let ((sep todos-done-separator)) 6248 (let ((sep todo-done-separator))
6249 (setq todos-done-separator (todos-done-separator)) 6249 (setq todo-done-separator (todo-done-separator))
6250 (save-match-data (todos-reset-done-separator sep))))) 6250 (save-match-data (todo-reset-done-separator sep)))))
6251 6251
6252(defun todos-modes-set-1 () 6252(defun todo-modes-set-1 ()
6253 "Make some settings that apply to multiple Todos modes." 6253 "Make some settings that apply to multiple Todo modes."
6254 (setq-local font-lock-defaults '(todos-font-lock-keywords t)) 6254 (setq-local font-lock-defaults '(todo-font-lock-keywords t))
6255 (setq-local tab-width todos-indent-to-here) 6255 (setq-local tab-width todo-indent-to-here)
6256 (setq-local indent-line-function 'todos-indent) 6256 (setq-local indent-line-function 'todo-indent)
6257 (when todos-wrap-lines 6257 (when todo-wrap-lines
6258 (visual-line-mode) 6258 (visual-line-mode)
6259 (setq wrap-prefix (make-string todos-indent-to-here 32)))) 6259 (setq wrap-prefix (make-string todo-indent-to-here 32))))
6260 6260
6261(defun todos-modes-set-2 () 6261(defun todo-modes-set-2 ()
6262 "Make some settings that apply to multiple Todos modes." 6262 "Make some settings that apply to multiple Todo modes."
6263 (add-to-invisibility-spec 'todos) 6263 (add-to-invisibility-spec 'todo)
6264 (setq buffer-read-only t) 6264 (setq buffer-read-only t)
6265 (when (boundp 'hl-line-range-function) 6265 (when (boundp 'hl-line-range-function)
6266 (setq-local hl-line-range-function 6266 (setq-local hl-line-range-function
6267 (lambda() (save-excursion 6267 (lambda() (save-excursion
6268 (when (todos-item-end) 6268 (when (todo-item-end)
6269 (cons (todos-item-start) 6269 (cons (todo-item-start)
6270 (todos-item-end)))))))) 6270 (todo-item-end))))))))
6271 6271
6272(defun todos-modes-set-3 () 6272(defun todo-modes-set-3 ()
6273 "Make some settings that apply to multiple Todos modes." 6273 "Make some settings that apply to multiple Todo modes."
6274 (setq-local todos-categories (todos-set-categories)) 6274 (setq-local todo-categories (todo-set-categories))
6275 (setq-local todos-category-number 1) 6275 (setq-local todo-category-number 1)
6276 (add-hook 'find-file-hook 'todos-display-as-todos-file nil t)) 6276 (add-hook 'find-file-hook 'todo-display-as-todo-file nil t))
6277 6277
6278(put 'todos-mode 'mode-class 'special) 6278(put 'todo-mode 'mode-class 'special)
6279 6279
6280(define-derived-mode todos-mode special-mode "Todos" 6280(define-derived-mode todo-mode special-mode "Todo"
6281 "Major mode for displaying, navigating and editing Todo lists. 6281 "Major mode for displaying, navigating and editing Todo lists.
6282 6282
6283\\{todos-mode-map}" 6283\\{todo-mode-map}"
6284 ;; (easy-menu-add todos-menu) 6284 ;; (easy-menu-add todo-menu)
6285 (todos-modes-set-1) 6285 (todo-modes-set-1)
6286 (todos-modes-set-2) 6286 (todo-modes-set-2)
6287 (todos-modes-set-3) 6287 (todo-modes-set-3)
6288 ;; Initialize todos-current-todos-file. 6288 ;; Initialize todo-current-todo-file.
6289 (when (member (file-truename (buffer-file-name)) 6289 (when (member (file-truename (buffer-file-name))
6290 (funcall todos-files-function)) 6290 (funcall todo-files-function))
6291 (setq-local todos-current-todos-file (file-truename (buffer-file-name)))) 6291 (setq-local todo-current-todo-file (file-truename (buffer-file-name))))
6292 (setq-local todos-show-done-only nil) 6292 (setq-local todo-show-done-only nil)
6293 (setq-local todos-categories-with-marks nil) 6293 (setq-local todo-categories-with-marks nil)
6294 (add-hook 'find-file-hook 'todos-add-to-buffer-list nil t) 6294 (add-hook 'find-file-hook 'todo-add-to-buffer-list nil t)
6295 (add-hook 'post-command-hook 'todos-update-buffer-list nil t) 6295 (add-hook 'post-command-hook 'todo-update-buffer-list nil t)
6296 (when todos-show-current-file 6296 (when todo-show-current-file
6297 (add-hook 'pre-command-hook 'todos-show-current-file nil t)) 6297 (add-hook 'pre-command-hook 'todo-show-current-file nil t))
6298 (add-hook 'window-configuration-change-hook 6298 (add-hook 'window-configuration-change-hook
6299 'todos-reset-and-enable-done-separator nil t) 6299 'todo-reset-and-enable-done-separator nil t)
6300 (add-hook 'kill-buffer-hook 'todos-reset-global-current-todos-file nil t)) 6300 (add-hook 'kill-buffer-hook 'todo-reset-global-current-todo-file nil t))
6301 6301
6302(put 'todos-archive-mode 'mode-class 'special) 6302(put 'todo-archive-mode 'mode-class 'special)
6303 6303
6304;; If todos-mode is parent, all todos-mode key bindings appear to be 6304;; If todo-mode is parent, all todo-mode key bindings appear to be
6305;; available in todos-archive-mode (e.g. shown by C-h m). 6305;; available in todo-archive-mode (e.g. shown by C-h m).
6306(define-derived-mode todos-archive-mode special-mode "Todos-Arch" 6306(define-derived-mode todo-archive-mode special-mode "Todo-Arch"
6307 "Major mode for archived Todos categories. 6307 "Major mode for archived Todo categories.
6308 6308
6309\\{todos-archive-mode-map}" 6309\\{todo-archive-mode-map}"
6310 (todos-modes-set-1) 6310 (todo-modes-set-1)
6311 (todos-modes-set-2) 6311 (todo-modes-set-2)
6312 (todos-modes-set-3) 6312 (todo-modes-set-3)
6313 (setq-local todos-current-todos-file (file-truename (buffer-file-name))) 6313 (setq-local todo-current-todo-file (file-truename (buffer-file-name)))
6314 (setq-local todos-show-done-only t)) 6314 (setq-local todo-show-done-only t))
6315 6315
6316(defun todos-mode-external-set () 6316(defun todo-mode-external-set ()
6317 "Set `todos-categories' externally to `todos-current-todos-file'." 6317 "Set `todo-categories' externally to `todo-current-todo-file'."
6318 (setq-local todos-current-todos-file todos-global-current-todos-file) 6318 (setq-local todo-current-todo-file todo-global-current-todo-file)
6319 (let ((cats (with-current-buffer 6319 (let ((cats (with-current-buffer
6320 ;; Can't use find-buffer-visiting when 6320 ;; Can't use find-buffer-visiting when
6321 ;; `todos-show-categories-table' is called on first 6321 ;; `todo-show-categories-table' is called on first
6322 ;; invocation of `todos-show', since there is then 6322 ;; invocation of `todo-show', since there is then
6323 ;; no buffer visiting the current file. 6323 ;; no buffer visiting the current file.
6324 (find-file-noselect todos-current-todos-file 'nowarn) 6324 (find-file-noselect todo-current-todo-file 'nowarn)
6325 (or todos-categories 6325 (or todo-categories
6326 ;; In Todos Edit mode todos-categories is now nil 6326 ;; In Todo Edit mode todo-categories is now nil
6327 ;; since it uses same buffer as Todos mode but 6327 ;; since it uses same buffer as Todo mode but
6328 ;; doesn't have the latter's local variables. 6328 ;; doesn't have the latter's local variables.
6329 (save-excursion 6329 (save-excursion
6330 (goto-char (point-min)) 6330 (goto-char (point-min))
6331 (read (buffer-substring-no-properties 6331 (read (buffer-substring-no-properties
6332 (line-beginning-position) 6332 (line-beginning-position)
6333 (line-end-position)))))))) 6333 (line-end-position))))))))
6334 (setq-local todos-categories cats))) 6334 (setq-local todo-categories cats)))
6335 6335
6336(define-derived-mode todos-edit-mode text-mode "Todos-Ed" 6336(define-derived-mode todo-edit-mode text-mode "Todo-Ed"
6337 "Major mode for editing multiline Todo items. 6337 "Major mode for editing multiline Todo items.
6338 6338
6339\\{todos-edit-mode-map}" 6339\\{todo-edit-mode-map}"
6340 (todos-modes-set-1) 6340 (todo-modes-set-1)
6341 (todos-mode-external-set) 6341 (todo-mode-external-set)
6342 (setq buffer-read-only nil)) 6342 (setq buffer-read-only nil))
6343 6343
6344(put 'todos-categories-mode 'mode-class 'special) 6344(put 'todo-categories-mode 'mode-class 'special)
6345 6345
6346(define-derived-mode todos-categories-mode special-mode "Todos-Cats" 6346(define-derived-mode todo-categories-mode special-mode "Todo-Cats"
6347 "Major mode for displaying and editing Todos categories. 6347 "Major mode for displaying and editing Todo categories.
6348 6348
6349\\{todos-categories-mode-map}" 6349\\{todo-categories-mode-map}"
6350 (todos-mode-external-set)) 6350 (todo-mode-external-set))
6351 6351
6352(put 'todos-filtered-items-mode 'mode-class 'special) 6352(put 'todo-filtered-items-mode 'mode-class 'special)
6353 6353
6354(define-derived-mode todos-filtered-items-mode special-mode "Todos-Fltr" 6354(define-derived-mode todo-filtered-items-mode special-mode "Todo-Fltr"
6355 "Mode for displaying and reprioritizing top priority Todos. 6355 "Mode for displaying and reprioritizing top priority Todo.
6356 6356
6357\\{todos-filtered-items-mode-map}" 6357\\{todo-filtered-items-mode-map}"
6358 (todos-modes-set-1) 6358 (todo-modes-set-1)
6359 (todos-modes-set-2)) 6359 (todo-modes-set-2))
6360 6360
6361(add-to-list 'auto-mode-alist '("\\.todo\\'" . todos-mode)) 6361(add-to-list 'auto-mode-alist '("\\.todo\\'" . todo-mode))
6362(add-to-list 'auto-mode-alist '("\\.toda\\'" . todos-archive-mode)) 6362(add-to-list 'auto-mode-alist '("\\.toda\\'" . todo-archive-mode))
6363(add-to-list 'auto-mode-alist '("\\.tod[tyr]\\'" . todos-filtered-items-mode)) 6363(add-to-list 'auto-mode-alist '("\\.tod[tyr]\\'" . todo-filtered-items-mode))
6364 6364
6365;; ----------------------------------------------------------------------------- 6365;; -----------------------------------------------------------------------------
6366(provide 'todos) 6366(provide 'todo-mode)
6367 6367
6368;;; todos.el ends here 6368;;; todo-mode.el ends here