(Yacc_suffixes, Asm_suffixes): Add some alternatives.
[bpt/emacs.git] / lisp / calendar / todo-mode.el
CommitLineData
da2ee685 1; todomode.el -- major mode for editing TODO list files
3cb152f9
OS
2
3;; ---------------------------------------------------------------------------
4
595b2334 5;; Copyright (C) 1997 Oliver Seidel
3cb152f9 6
595b2334
OS
7;; Keywords: todo-mode
8
9;; todomode.el is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation; either version 2, or (at your option)
12;; any later version.
13
14;; todomode.el is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs; see the file COPYING. If not, write to the
21;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22;; Boston, MA 02111-1307, USA.
23
24;; ---------------------------------------------------------------------------
25
26;; Contact information: (address) O Seidel, Lessingstr 8, Eschborn, FRG
27;; (e-mail ) Oliver.Seidel@cl.cam.ac.uk (2 Aug 1997)
28
29;; $Id: todomode.el,v 1.7 1997/08/05 22:34:14 os10000 Exp os10000 $
3cb152f9 30;;
384af0dd 31;; $Log: todomode.el,v $
595b2334
OS
32;; Revision 1.7 1997/08/05 22:34:14 os10000
33;; Fixed insertion routine with help from Trey Jackson
34;; <tjackson@ichips.intel.com>; added todo-ins-thresh;
35;; fixed keyboard layout to remove unwanted keys.
36;;
3d8105fb
OS
37;; Revision 1.6 1997/08/05 16:47:01 os10000
38;; Incorporated menus for XEmacs from Allan.Cochrane@soton.sc.philips.com,
39;; fixed TYPO, fixed todo-file-cmd, cleaned up rcs history.
40;;
a360be79
OS
41;; Revision 1.5 1997/08/05 14:43:39 os10000
42;; Added improvements from Ron Gut <rgut@aware.com>.
43;; Added category management.
44;;
da2ee685
OS
45;; Revision 1.4 1997/08/04 16:18:45 os10000
46;; Added Raise/Lower item.
47;;
8cdc3b3d
OS
48;; Revision 1.3 1997/08/03 12:47:26 os10000
49;; Cleaned up variables, prefix and cursor position.
50;;
7e6ed9b9
OS
51;; Revision 1.2 1997/08/03 12:15:28 os10000
52;; It appears to work.
53;;
384af0dd
OS
54;; Revision 1.1 1997/08/03 12:15:13 os10000
55;; Initial revision
3cb152f9
OS
56;;
57
58;; ---------------------------------------------------------------------------
59
7e6ed9b9
OS
60;; Description:
61;;
62;; To get this to work, make emacs execute the line "(require 'todomode)"
63;; and maybe initialise the variables below on startup.
64;;
a360be79
OS
65;; Just for the case that you are wondering about the ugly name of this
66;; package: I am one of those unfortunate people who have DOS, LINUX and
67;; OS/2 on one of their computers, so part of my home-filespace is shared
68;; and stored on a DOS partition, which is accessible to all systems. If
69;; you wish, you can of course rename the name of the file (and the last
70;; command) to something more aisthetically (please don't argue about
71;; this spelling ...) pleasing, like i.e. todo-mode.
72;;
73;; You will have the following facilities available:
7e6ed9b9
OS
74;;
75;; M-x todo-mode will enter the todo list screen, here type
da2ee685
OS
76;; + to go to next category
77;; - to go to previous category
7e6ed9b9 78;; e to edit the current entry
da2ee685
OS
79;; f to file the current entry, including a
80;; comment and timestamp
7e6ed9b9
OS
81;; i to insert a new entry
82;; k to kill the current entry
8cdc3b3d 83;; l lower the current entry's priority
da2ee685
OS
84;; n for the next entry
85;; p for the previous entry
86;; q to save the list and exit the buffer
87;; r raise current entryk's priority
88;; s to save the list
7e6ed9b9 89;;
a360be79
OS
90;; When you add a new entry, you are asked for the text and then for the
91;; category. I for example have categories for things that I want to do
92;; in the office (like mail my mum), that I want to do in town (like buy
93;; cornflakes) and things I want to do at home (move my suitcases). The
94;; categories can be selected with the cursor keys and if you type in the
95;; name of a category which didn't exist before, an empty category of the
96;; desired name will be added.
97;;
7e6ed9b9
OS
98;; I would recommend to add the following bindings to your global keymap:
99;;
da2ee685 100;; (global-set-key "\C-ct" 'todo-show)
7e6ed9b9
OS
101;; (global-set-key "\C-ci" 'todo-cmd-inst)
102;;
103;; This will enable you to quickly find the todo-list, or to simply add an
104;; entry, without changing to it and getting sidetracked from your current
105;; project.
106;;
da2ee685
OS
107;; I would also recommend that use the prefix "*/*" (by leaving the
108;; variable 'todo-prefix' untouched) so that the diary displays
109;; each entry every day.
a360be79
OS
110;;
111;; For this, please read the documentation that goes with the calendar
112;; since that will tell you how you can set up the fancy diary display
113;; and use the #include command to include your todo list file as part
114;; of your diary.
115;;
3d8105fb
OS
116;; Another nifty feature is the insertion accuracy. If you have 8 items
117;; in your TODO list, then you may get asked 4 questions by the binary
118;; insertion algorithm. However, you may not really have a need for such
119;; accurate priorities amongst your TODO items. If you now think about
120;; the binary insertion halfing the size of the window each time, then
121;; the threshhold is the window size at which it will stop. If you set
122;; the threshhold to zero, the upper and lower bound will coincide at the
123;; end of the loop and you will insert your item just before that point.
124;; If you set the threshhold to i.e. 8, it will stop as soon as the window
125;; size drops below that amount and will insert the item in the approximate
126;; centre of that window. I got the idea for this feature after reading
127;; a very helpful e-mail reply from Trey Jackson <tjackson@ichips.intel.com>
128;; who corrected some of my awful coding and pointed me towards some good
129;; reading. Thanks Trey!
130;;
a360be79
OS
131;; Enjoy this package and express your gratitude by sending valuables
132;; to my parents' address as listed above!!!
133;;
134;; Oliver Seidel
7e6ed9b9
OS
135
136;; ---------------------------------------------------------------------------
137
3cb152f9
OS
138;; User-configurable variables:
139
da2ee685 140(defvar todo-prefix "*/*" "TODO mode prefix when creating entries")
7e6ed9b9 141(defvar todo-file-do "~/.todo-do" "TODO mode filename of list file")
3cb152f9
OS
142(defvar todo-file-done "~/.todo-done" "TODO mode filename of archive file")
143(defvar todo-mode-hook nil "Hooks invoked when the *TODO* buffer is created.")
3d8105fb 144(defvar todo-ins-thresh 0 "TODO mode insertion accuracy.")
3cb152f9
OS
145
146;; ---------------------------------------------------------------------------
147
148(require 'time-stamp)
149
3d8105fb
OS
150(defvar todo-mode-map nil "TODO mode keymap. See `todo-mode'")
151(if todo-mode-map
152 nil
153 (let ((map (make-keymap)))
154 (suppress-keymap map t)
155 (define-key map "+" 'todo-cmd-forw)
156 (define-key map "-" 'todo-cmd-back)
157 (define-key map "e" 'todo-cmd-edit)
158 (define-key map "f" 'todo-cmd-file)
159 (define-key map "i" 'todo-cmd-inst)
160 (define-key map "k" 'todo-cmd-kill)
161 (define-key map "l" 'todo-cmd-lowr)
162 (define-key map "n" 'todo-cmd-next)
163 (define-key map "p" 'todo-cmd-prev)
164 (define-key map "q" 'todo-cmd-done)
165 (define-key map "r" 'todo-cmd-rais)
166 (define-key map "s" 'todo-cmd-save)
167 (setq todo-mode-map map)))
3cb152f9 168
da2ee685
OS
169(defun todo-cat-slct ()
170 (let ((todo-category-name (nth todo-category-number todo-cats)))
171 (setq mode-line-buffer-identification (concat "Category: " todo-category-name))
172 (widen)
173 (goto-char (point-min))
174 (search-forward (concat "--- " todo-category-name))
175 (setq begin (+ (point-at-eol) 1))
176 (search-forward "--- End")
177 (narrow-to-region begin (point-at-bol))
178 (goto-char (point-min))
179 )
180)
181
182(defun todo-cmd-forw () "Go forward to TODO list of next category."
183 (interactive)
184 (let ((todo-cat-cnt (- (length todo-cats) 1)))
185 (setq todo-category-number (if (< todo-category-number todo-cat-cnt)
186 (+ todo-category-number 1) 0))
187 (todo-cat-slct)
188 )
189 )
190
191(defun todo-cmd-back () "Go back to TODO list of previous category."
192 (interactive)
193 (let ((todo-cat-cnt (- (length todo-cats) 1)))
194 (setq todo-category-number (if (> todo-category-number 0)
195 (- todo-category-number 1) todo-cat-cnt))
196 (todo-cat-slct)
197 )
198 )
199
200(defun todo-cmd-prev () "Select previous entry of TODO list."
3cb152f9
OS
201 (interactive)
202 (forward-line -1)
203 (beginning-of-line nil)
204 (message "")
205 )
206
da2ee685 207(defun todo-cmd-next () "Select next entry of TODO list."
3cb152f9
OS
208 (interactive)
209 (forward-line 1)
210 (beginning-of-line nil)
211 (message "")
212 )
213
da2ee685
OS
214(defun todo-cmd-save () "Save the TODO list."
215 (interactive)
216 (save-buffer)
217 )
218
219(defun todo-cmd-done () "Done with TODO list for now."
3cb152f9 220 (interactive)
da2ee685
OS
221 (widen)
222 (save-buffer)
7e6ed9b9
OS
223 (beginning-of-line nil)
224 (message "")
3cb152f9
OS
225 (bury-buffer)
226 )
227
da2ee685 228(defun todo-line () "Find current line in buffer." (buffer-substring (point-at-bol) (point-at-eol)))
3cb152f9 229
da2ee685 230(defun todo-cmd-edit () "Edit current TODO list entry."
3cb152f9 231 (interactive)
da2ee685
OS
232 (let ((todo-entry (todo-line)))
233 (delete-region (point-at-bol) (point-at-eol))
234 (insert (read-from-minibuffer "Edit: " todo-entry))
235 (beginning-of-line nil)
236 (message "")
237 )
3cb152f9
OS
238 )
239
240(defvar todo-prv-lne 0 "previous line that I asked about.")
241(defvar todo-prv-ans 0 "previous answer that I got.")
242
da2ee685 243(defun todo-add-category (cat) "Add a new category to the TODO list."
3cb152f9 244 (interactive)
3cb152f9 245 (save-window-excursion
da2ee685 246 (setq todo-cats (cons cat todo-cats))
3cb152f9 247 (find-file todo-file-do)
da2ee685
OS
248 (widen)
249 (goto-char (point-min))
250 (let ((posn (search-forward "-*- mode: todo; " 17 t)))
251 (if (not (null posn)) (goto-char posn))
252 (if (equal posn nil) (progn (insert "-*- mode: todo; \n") (forward-char -1)) (kill-line))
3cb152f9 253 )
da2ee685
OS
254 (insert (format "todo-cats: %S; -*-" todo-cats))
255 (forward-char 1)
256 (insert (format "%s --- %s\n--- End\n%s %s\n" todo-prefix cat todo-prefix (make-string 75 ?-)))
3cb152f9 257 )
da2ee685
OS
258 0
259 )
260
3d8105fb
OS
261(defun todo-cmd-inst ()
262 "Insert new TODO list entry."
da2ee685 263 (interactive)
3cb152f9 264 (beginning-of-line nil)
da2ee685 265 (let* ((todo-entry (concat todo-prefix " " (read-from-minibuffer "New TODO entry: ")))
3d8105fb
OS
266 (temp-catgs todo-cats)
267 (todo-hstry (cons 'temp-catgs (+ todo-category-number 1))))
da2ee685
OS
268 (save-window-excursion
269 (setq todo-category
3d8105fb
OS
270 (read-from-minibuffer "Category: "
271 (nth todo-category-number todo-cats)
272 nil nil todo-hstry))
273
274 (let ((cat-exists (member todo-category todo-cats)))
275 (setq todo-category-number
276 (if cat-exists
277 (- (length todo-cats) (length cat-exists))
278 (todo-add-category todo-category))))
da2ee685
OS
279 (todo-show)
280 (setq todo-prv-lne 0)
3d8105fb
OS
281
282 (let ((todo-fst 1)
283 (todo-lst (+ 1 (count-lines (point-min) (point-max)))))
284 (while (> (- todo-lst todo-fst) todo-ins-thresh)
285 (let* ((todo-cur (/ (+ todo-fst todo-lst) 2))
286 (todo-ans (if (< todo-cur todo-lst) (todo-ask todo-cur) nil)))
287 (if todo-ans
288 (setq todo-lst todo-cur)
289 (setq todo-fst (+ todo-cur 1)))))
290
291 (setq todo-fst (/ (+ todo-fst todo-lst) 2))
292 ;; goto-line doesn't have the desired behavior in a narrowed buffer
293 (goto-char (point-min))
294 (message (format "todo-fst=%d" todo-fst))
295 (forward-line (- todo-fst 1)))
296
da2ee685 297 (insert (concat todo-entry "\n"))
3d8105fb 298 (forward-line -1))
da2ee685 299 (beginning-of-line nil)
3d8105fb
OS
300 (message "")))
301
302(defun todo-ask (lne)
303 "Ask whether entry is more important than at LNE."
304 (if (not (equal todo-prv-lne lne))
305 (progn
306 (setq todo-prv-lne lne)
307 (goto-char (point-min))
308 (forward-line (- todo-prv-lne 1))
309 (setq todo-prv-ans (y-or-n-p (concat "More important than '" (todo-line) "'? ")))))
310 todo-prv-ans)
3cb152f9 311
da2ee685 312(defun todo-cmd-kill () "Delete current TODO list entry."
3cb152f9
OS
313 (interactive)
314 (if (> (count-lines (point-min) (point-max)) 0)
315 (progn
da2ee685
OS
316 (let* ((todo-entry (todo-line))
317 (todo-answer (y-or-n-p (concat "Permanently remove '" todo-entry "'? "))))
318 (if todo-answer
319 (progn
320 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
321 (forward-line -1)
322 )
323 )
324 )
325 (message "")
3cb152f9 326 )
da2ee685 327 (message "No TODO list entry to delete.")
3cb152f9
OS
328 )
329 (beginning-of-line nil)
3cb152f9
OS
330 )
331
8cdc3b3d
OS
332(defun todo-cmd-rais () "Raise priority of current entry."
333 (interactive)
334 (if (> (count-lines (point-min) (point-max)) 0)
335 (progn
336 (setq todo-entry (todo-line))
da2ee685 337 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
8cdc3b3d
OS
338 (forward-line -1)
339 (insert (concat todo-entry "\n"))
da2ee685
OS
340 (forward-line -1)
341 (message "")
8cdc3b3d 342 )
da2ee685 343 (message "No TODO list entry to raise.")
8cdc3b3d
OS
344 )
345 (beginning-of-line nil)
8cdc3b3d
OS
346 )
347
348(defun todo-cmd-lowr () "Lower priority of current entry."
349 (interactive)
350 (if (> (count-lines (point-min) (point-max)) 0)
351 (progn
352 (setq todo-entry (todo-line))
da2ee685 353 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
8cdc3b3d
OS
354 (forward-line 1)
355 (insert (concat todo-entry "\n"))
da2ee685
OS
356 (forward-line -1)
357 (message "")
8cdc3b3d 358 )
da2ee685 359 (message "No TODO list entry to raise.")
8cdc3b3d
OS
360 )
361 (beginning-of-line nil)
8cdc3b3d
OS
362 )
363
da2ee685 364(defun todo-cmd-file () "File away the current TODO list entry."
3cb152f9
OS
365 (interactive)
366 (if (> (count-lines (point-min) (point-max)) 0)
367 (progn
da2ee685
OS
368 (let ((time-stamp-format "%3b %2d, %y, %02I:%02M%p"))
369 (beginning-of-line nil)
a360be79 370 (delete-region (point-at-bol) (search-forward todo-prefix))
da2ee685
OS
371 (insert (time-stamp-string))
372 (end-of-line nil)
a360be79 373 (insert (concat " (" (read-from-minibuffer "Comment: ") ")"))
da2ee685
OS
374 (append-to-file (point-at-bol) (+ 1 (point-at-eol)) todo-file-done)
375 (delete-region (point-at-bol) (+ 1 (point-at-eol)))
376 (forward-line -1)
377 )
378 (message "")
3cb152f9 379 )
da2ee685 380 (message "No TODO list entry to delete.")
3cb152f9
OS
381 )
382 (beginning-of-line nil)
3cb152f9
OS
383 )
384
385;; ---------------------------------------------------------------------------
386
da2ee685
OS
387;; utility functions: These are available in XEmacs, but not in Emacs 19.34
388
389(if (not (fboundp 'point-at-bol))
390 (defun point-at-bol ()
391 (save-excursion
392 (beginning-of-line)
393 (point))))
394
395(if (not (fboundp 'point-at-eol))
396 (defun point-at-eol ()
397 (save-excursion
398 (end-of-line)
399 (point))))
400
401;; ---------------------------------------------------------------------------
402
a360be79
OS
403(defvar todo-mode-popup-menu
404 (purecopy '("Todo Mode Menu"
405 ["Forward item" todo-cmd-forw t]
406 ["Backward item" todo-cmd-back t]
407 "---"
408 ["Edit item" todo-cmd-edit t]
409 ["File item" todo-cmd-file t]
410 ["Insert new item" todo-cmd-inst t]
411 ["Kill item" todo-cmd-kill t]
412 "---"
413 ["Lower item priority" todo-cmd-lowr t]
414 ["Raise item priority" todo-cmd-rais t]
415 "---"
416 ["Next item" todo-cmd-next t]
417 ["Previous item" todo-cmd-prev t]
418 "---"
419 ["Save" todo-cmd-save t]
420 "---"
421 ["Quit" todo-cmd-done t]
422 )
423 )
424 )
425
da2ee685
OS
426(defvar todo-cats nil "TODO categories.")
427(defvar todo-category-number 0 "TODO category number.")
428
429(defun todo-mode () "Major mode for editing TODO lists.\n\n\\{todo-mode-map}"
3cb152f9 430 (interactive)
3cb152f9
OS
431 (setq major-mode 'todo-mode)
432 (setq mode-name "TODO")
433 (use-local-map todo-mode-map)
a360be79 434 (setq mode-popup-menu todo-mode-popup-menu)
da2ee685
OS
435 (run-hooks 'todo-mode-hook)
436 )
437
438(defun todo-show () "Show TODO list."
439 (interactive)
440 (find-file todo-file-do)
441 (if (null todo-cats)
442 (progn
443 (todo-add-category "Todo")
444 (goto-char (point-min))
445 (goto-char (search-forward "--- End"))
446 (let ((bol (point-at-bol)))
447 (forward-line 1)
448 (let* ((eol (+ (point-at-eol) 1))
449 (mrkr (buffer-substring bol eol)))
450 (delete-region bol eol)
451 (goto-char (point-max))
452 (insert mrkr)
453 )
454 )
455 (save-buffer)
456 (kill-buffer (current-buffer))
457 (find-file todo-file-do)
458 )
459 )
7e6ed9b9 460 (beginning-of-line nil)
da2ee685
OS
461 (todo-cat-slct)
462 )
3cb152f9
OS
463
464(provide 'todomode)
465
466;; ---------------------------------------------------------------------------
467
da2ee685 468;; todomode.el ends here
3cb152f9
OS
469
470;; ---------------------------------------------------------------------------