("Czech"): Fix the documentation.
[bpt/emacs.git] / lisp / calendar / todo-mode.el
CommitLineData
e8af40ee 1;;; todo-mode.el --- major mode for editing TODO list files
3cb152f9 2
bf2fcd67 3;; Copyright (C) 1997, 1999, 2001 Free Software Foundation, Inc.
3cb152f9 4
cb9222cf
DL
5;; Author: Oliver Seidel <os10000@seidel-space.de>
6;; [Not clear the above works, July 2000]
7c896f63 7;; Created: 2 Aug 1997
4f9b5264 8;; Version: $Id: todo-mode.el,v 1.49 2001/11/17 04:01:31 rms Exp $
cb9222cf 9;; Keywords: calendar, todo
3cb152f9 10
7c896f63 11;; This file is part of GNU Emacs.
595b2334 12
7c896f63 13;; GNU Emacs is free software; you can redistribute it and/or modify
595b2334
OS
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
7c896f63
OS
17
18;; GNU Emacs is distributed in the hope that it will be useful,
595b2334
OS
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
7c896f63 22
595b2334
OS
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING. If not, write to the
25;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
27
28;; ---------------------------------------------------------------------------
29
7c896f63
OS
30;;; Commentary:
31
85b3b166
OS
32;; Mode Description
33;;
34;; TODO is a major mode for EMACS which offers functionality to
35;; treat most lines in one buffer as a list of items one has to
36;; do. There are facilities to add new items, which are
37;; categorised, to edit or even delete items from the buffer.
38;; The buffer contents are currently compatible with the diary,
39;; so that the list of todo-items will show up in the FANCY diary
40;; mode.
41;;
42;; Notice: Besides the major mode, this file also exports the
43;; function `todo-show' which will change to the one specific
44;; TODO file that has been specified in the todo-file-do
45;; variable. If this file does not conform to the TODO mode
46;; conventions, the todo-show function will add the appropriate
47;; header and footer. I don't anticipate this to cause much
48;; grief, but be warned, in case you attempt to read a plain text
49;; file.
50;;
01b864bc 51;; Preface, Quickstart Installation
595b2334 52;;
e4541b67 53;; To get this to work, make emacs execute the line
3d8105fb 54;;
e4541b67 55;; (autoload 'todo-mode "todo-mode"
4cbc9d5a
OS
56;; "Major mode for editing TODO lists." t)
57;; (autoload 'todo-show "todo-mode"
58;; "Show TODO items." t)
59;; (autoload 'todo-insert-item "todo-mode"
60;; "Add TODO item." t)
7c896f63 61;;
e4541b67
OS
62;; You may now enter new items by typing "M-x todo-insert-item",
63;; or enter your TODO list file by typing "M-x todo-show".
64;;
65;; The TODO list file has a special format and some auxiliary
66;; information, which will be added by the todo-show function if
67;; it attempts to visit an un-initialised file. Hence it is
68;; recommended to use the todo-show function for the first time,
69;; in order to initialise the file, but it is not necessary
70;; afterwards.
71;;
72;; As these commands are quite long to type, I would recommend
73;; the addition of two bindings to your to your global keymap. I
74;; personally have the following in my initialisation file:
75;;
76;; (global-set-key "\C-ct" 'todo-show) ;; switch to TODO buffer
01b864bc 77;; (global-set-key "\C-ci" 'todo-insert-item) ;; insert new item
7c896f63 78;;
e4541b67
OS
79;; Note, however, that this recommendation has prompted some
80;; criticism, since the keys C-c LETTER are reserved for user
81;; functions. I believe my recommendation is acceptable, since
82;; the Emacs Lisp Manual *Tips* section also details that the
83;; mode itself should not bind any functions to those keys. The
84;; express aim of the above two bindings is to work outside the
85;; mode, which doesn't need the show function and offers a
86;; different binding for the insert function. They serve as
87;; shortcuts and are not even needed (since the TODO mode will be
88;; entered by visiting the TODO file, and later by switching to
89;; its buffer).
90;;
5d035cad
OS
91;; If you are an advanced user of this package, please consult
92;; the whole source code for autoloads, because there are several
93;; extensions that are not explicitly listed in the above quick
94;; installation.
95;;
e4541b67 96;; Version
7c896f63 97;;
e4541b67 98;; Which version of todo-mode.el does this documentation refer to?
579e1c67 99;;
4f9b5264 100;; $Id: todo-mode.el,v 1.49 2001/11/17 04:01:31 rms Exp $
cb9222cf
DL
101;;
102;; Pre-Requisites
103;;
104;; This package will require the following packages to be
105;; available on the load-path:
106;;
107;; time-stamp
108;; easymenu
01b864bc 109;;
e4541b67 110;; Operation
01b864bc
OS
111;;
112;; You will have the following facilities available:
113;;
114;; M-x todo-show will enter the todo list screen, here type
115;;
116;; + to go to next category
117;; - to go to previous category
e4541b67
OS
118;; d to file the current entry, including a
119;; comment and timestamp
01b864bc 120;; e to edit the current entry
1966902e 121;; E to edit a multi-line entry
01b864bc
OS
122;; f to file the current entry, including a
123;; comment and timestamp
4dc1a160 124;; i to insert a new entry, with prefix, omit category
1966902e 125;; I to insert a new entry at current cursor position
e4541b67 126;; j jump to category
01b864bc
OS
127;; k to kill the current entry
128;; l to lower the current entry's priority
129;; n for the next entry
130;; p for the previous entry
e4541b67 131;; P print
01b864bc
OS
132;; q to save the list and exit the buffer
133;; r to raise the current entry's priority
134;; s to save the list
f1757bdd 135;; S to save the list of top priorities
85b3b166 136;; t show top priority items for each category
01b864bc 137;;
e4541b67
OS
138;; When you add a new entry, you are asked for the text and then
139;; for the category. I for example have categories for things
140;; that I want to do in the office (like mail my mum), that I
141;; want to do in town (like buy cornflakes) and things I want to
142;; do at home (move my suitcases). The categories can be
143;; selected with the cursor keys and if you type in the name of a
144;; category which didn't exist before, an empty category of the
145;; desired name will be added and filled with the new entry.
01b864bc
OS
146;;
147;; Configuration
148;;
149;; Variable todo-prefix
150;;
151;; I would like to recommend that you use the prefix "*/*" (by
e4541b67
OS
152;; leaving the variable 'todo-prefix' untouched) so that the
153;; diary displays each entry every day.
01b864bc 154;;
e4541b67
OS
155;; To understand what I mean, please read the documentation that
156;; goes with the calendar since that will tell you how you can
157;; set up the fancy diary display and use the #include command to
158;; include your todo list file as part of your diary.
01b864bc 159;;
e4541b67
OS
160;; If you have the diary package set up to usually display more
161;; than one day's entries at once, consider using
01b864bc
OS
162;;
163;; "&%%(equal (calendar-current-date) date)"
164;;
e4541b67
OS
165;; as the value of `todo-prefix'. Please note that this may slow
166;; down the processing of your diary file some.
01b864bc 167;;
5d035cad
OS
168;; Carsten Dominik <dominik@strw.LeidenUniv.nl> suggested that
169;;
170;; "&%%(todo-cp)"
171;;
172;; might be nicer and to that effect a function has been declared
173;; further down in the code. You may wish to auto-load this.
174;;
175;; Carsten also writes that that *changing* the prefix after the
176;; todo list is already established is not as simple as changing
177;; the variable - the todo files have to be changed by hand.
178;;
01b864bc
OS
179;; Variable todo-file-do
180;;
e4541b67
OS
181;; This variable is fairly self-explanatory. You have to store
182;; your TODO list somewhere. This variable tells the package
183;; where to go and find this file.
01b864bc
OS
184;;
185;; Variable todo-file-done
186;;
e4541b67
OS
187;; Even when you're done, you may wish to retain the entries.
188;; Given that they're timestamped and you are offered to add a
189;; comment, this can make a useful diary of past events. It will
190;; even blend in with the EMACS diary package. So anyway, this
191;; variable holds the name of the file for the filed todo-items.
01b864bc 192;;
f1757bdd
OS
193;; Variable todo-file-top
194;;
195;; File storing the top priorities of your TODO list when
196;; todo-save-top-priorities is non-nil. Nice to include in your
197;; diary instead of the complete TODO list.
198;;
01b864bc
OS
199;; Variable todo-mode-hook
200;;
e4541b67
OS
201;; Just like other modes, too, this mode offers to call your
202;; functions before it goes about its business. This variable
203;; will be inspected for any functions you may wish to have
204;; called once the other TODO mode preparations have been
205;; completed.
206;;
207;; Variable todo-insert-threshold
208;;
209;; Another nifty feature is the insertion accuracy. If you have
210;; 8 items in your TODO list, then you may get asked 4 questions
211;; by the binary insertion algorithm. However, you may not
212;; really have a need for such accurate priorities amongst your
213;; TODO items. If you now think about the binary insertion
214;; halfing the size of the window each time, then the threshhold
215;; is the window size at which it will stop. If you set the
216;; threshhold to zero, the upper and lower bound will coincide at
217;; the end of the loop and you will insert your item just before
85b3b166 218;; that point. If you set the threshhold to, e.g. 8, it will stop
e4541b67
OS
219;; as soon as the window size drops below that amount and will
220;; insert the item in the approximate centre of that window. I
221;; got the idea for this feature after reading a very helpful
222;; e-mail reply from Trey Jackson <trey@cs.berkeley.edu> who
223;; corrected some of my awful coding and pointed me towards some
224;; good reading. Thanks Trey!
01b864bc
OS
225;;
226;; Things to do
227;;
e4541b67
OS
228;; These originally were my ideas, but now also include all the
229;; suggestions that I included before forgetting them:
230;;
e4541b67
OS
231;; o Fancy fonts for todo/top-priority buffer
232;; o Remove todo-prefix option in todo-top-priorities
233;; o Rename category
234;; o Move entry from one category to another one
235;; o Entries which both have the generic */* prefix and a
236;; "deadline" entry which are understood by diary, indicating
237;; an event (unless marked by &)
238;; o The optional COUNT variable of todo-forward-item should be
239;; applied to the other functions performing similar tasks
240;; o Modularization could be done for repeaded elements of
dad8ca4c 241;; the code, like the completing-read lines of code.
e4541b67 242;; o license / version function
01b864bc
OS
243;; o export to diary file
244;; o todo-report-bug
245;; o GNATS support
e4541b67
OS
246;; o elide multiline (as in bbdb, or, to a lesser degree, in
247;; outline mode)
248;; o rewrite complete package to store data as lisp objects
249;; and have display modes for display, for diary export,
250;; etc. (Richard Stallman pointed out this is a bad idea)
251;; o so base todo-mode.el on generic-mode.el instead
01b864bc 252;;
e4541b67 253;; History and Gossip
01b864bc 254;;
e4541b67
OS
255;; Many thanks to all the ones who have contributed to the
256;; evolution of this package! I hope I have listed all of you
257;; somewhere in the documentation or at least in the RCS history!
01b864bc 258;;
e4541b67
OS
259;; Enjoy this package and express your gratitude by sending nice
260;; things to my parents' address!
01b864bc
OS
261;;
262;; Oliver Seidel
e4541b67 263;; (Lessingstr. 8, 65760 Eschborn, Federal Republic of Germany)
7e6ed9b9 264
7c896f63
OS
265;;; Code:
266
4f9b5264
PJ
267(require 'time-stamp)
268
269
3cb152f9
OS
270;; User-configurable variables:
271
85b3b166
OS
272(defgroup todo nil
273 "Maintain a list of todo items."
ade0f0b8
DL
274 :link '(emacs-commentary-link "todo-mode")
275 :version "21.1"
85b3b166
OS
276 :group 'calendar)
277
cb9222cf 278(defcustom todo-prefix "*/*"
85b3b166
OS
279 "*TODO mode prefix for entries.
280
281This is useful in conjunction with `calendar' and `diary' if you use
282
283#include \"~/.todo-do\"
284
285in your diary file to include your todo list file as part of your
286diary. With the default value \"*/*\" the diary displays each entry
287every day and it may also be marked on every day of the calendar.
288Using \"&%%(equal (calendar-current-date) date)\" instead will only
289show and mark todo entreis for today, but may slow down processing of
290the diary file somewhat."
291 :type 'string
292 :group 'todo)
cb9222cf 293(defcustom todo-file-do "~/.todo-do"
85b3b166
OS
294 "*TODO mode list file."
295 :type 'file
296 :group 'todo)
cb9222cf 297(defcustom todo-file-done "~/.todo-done"
85b3b166
OS
298 "*TODO mode archive file."
299 :type 'file
300 :group 'todo)
301(defcustom todo-mode-hook nil
302 "*TODO mode hooks."
303 :type 'hook
304 :group 'todo)
305(defcustom todo-edit-mode-hook nil
306 "*TODO Edit mode hooks."
307 :type 'hook
308 :group 'todo)
309(defcustom todo-insert-threshold 0
310 "*TODO mode insertion accuracy.
311
312If you have 8 items in your TODO list, then you may get asked 4
313questions by the binary insertion algorithm. However, you may not
314really have a need for such accurate priorities amongst your TODO
315items. If you now think about the binary insertion halfing the size
316of the window each time, then the threshhold is the window size at
317which it will stop. If you set the threshhold to zero, the upper and
318lower bound will coincide at the end of the loop and you will insert
319your item just before that point. If you set the threshhold to,
320e.g. 8, it will stop as soon as the window size drops below that
321amount and will insert the item in the approximate centre of that
322window."
323 :type 'integer
324 :group 'todo)
cb9222cf
DL
325(defvar todo-edit-buffer " *TODO Edit*"
326 "TODO Edit buffer name.")
85b3b166 327(defcustom todo-file-top "~/.todo-top"
f1757bdd 328 "*TODO mode top priorities file.
85b3b166 329
f1757bdd 330Not in TODO format, but diary compatible.
85b3b166
OS
331Automatically generated when `todo-save-top-priorities' is non-nil."
332 :type 'string
333 :group 'todo)
334
335(defcustom todo-print-function 'ps-print-buffer-with-faces
336 "*Function to print the current buffer."
337 :type 'symbol
338 :group 'todo)
339(defcustom todo-show-priorities 1
340 "*Default number of priorities to show by \\[todo-top-priorities].
3410 means show all entries."
342 :type 'integer
343 :group 'todo)
344(defcustom todo-print-priorities 0
345 "*Default number of priorities to print by \\[todo-print].
3460 means print all entries."
347 :type 'integer
348 :group 'todo)
349(defcustom todo-remove-separator t
cb9222cf 350 "*Non-nil to remove category separators in\
85b3b166
OS
351\\[todo-top-priorities] and \\[todo-print]."
352 :type 'boolean
353 :group 'todo)
354(defcustom todo-save-top-priorities-too t
a554b301 355 "*Non-nil makes `todo-save' automatically save top-priorities in `todo-file-top'."
85b3b166
OS
356 :type 'boolean
357 :group 'todo)
e4541b67 358
7c896f63
OS
359;; Thanks for the ISO time stamp format go to Karl Eichwalder <ke@suse.de>
360;; My format string for the appt.el package is "%3b %2d, %y, %02I:%02M%p".
361;;
85b3b166 362(defcustom todo-time-string-format
cb9222cf 363 "%:y-%02m-%02d %02H:%02M"
85b3b166
OS
364 "*TODO mode time string format for done entries.
365For details see the variable `time-stamp-format'."
366 :type 'string
367 :group 'todo)
368
369(defcustom todo-entry-prefix-function 'todo-entry-timestamp-initials
370 "*Function producing text to insert at start of todo entry."
371 :type 'symbol
372 :group 'todo)
373(defcustom todo-initials (or (getenv "INITIALS") (user-login-name))
374 "*Initials of todo item author."
375 :type 'string
376 :group 'todo)
e4541b67
OS
377
378(defun todo-entry-timestamp-initials ()
f1757bdd 379 "Prepend timestamp and your initials to the head of a TODO entry."
e4541b67
OS
380 (let ((time-stamp-format todo-time-string-format))
381 (concat (time-stamp-string) " " todo-initials ": ")))
382
cb9222cf
DL
383;; ---------------------------------------------------------------------------
384
cf1ebf43
OS
385;; Set up some helpful context ...
386
9278c60d
DL
387(defvar todo-categories nil
388 "TODO categories.")
cb9222cf 389
9278c60d 390(defvar todo-cats nil
85b3b166
OS
391 "Old variable for holding the TODO categories.
392Use `todo-categories' instead.")
e4541b67 393
cb9222cf
DL
394(defvar todo-previous-line 0
395 "Previous line asked about.")
e4541b67 396
cb9222cf
DL
397(defvar todo-previous-answer 0
398 "Previous answer got.")
cf1ebf43 399
9278c60d 400(defvar todo-mode-map
3d8105fb
OS
401 (let ((map (make-keymap)))
402 (suppress-keymap map t)
7f6241ea
OS
403 (define-key map "+" 'todo-forward-category)
404 (define-key map "-" 'todo-backward-category)
1966902e 405 (define-key map "d" 'todo-file-item) ;done/delete
7f6241ea
OS
406 (define-key map "e" 'todo-edit-item)
407 (define-key map "E" 'todo-edit-multiline)
408 (define-key map "f" 'todo-file-item)
409 (define-key map "i" 'todo-insert-item)
d145aa83 410 (define-key map "I" 'todo-insert-item-here)
e4541b67 411 (define-key map "j" 'todo-jump-to-category)
7f6241ea
OS
412 (define-key map "k" 'todo-delete-item)
413 (define-key map "l" 'todo-lower-item)
414 (define-key map "n" 'todo-forward-item)
415 (define-key map "p" 'todo-backward-item)
e4541b67 416 (define-key map "P" 'todo-print)
7f6241ea
OS
417 (define-key map "q" 'todo-quit)
418 (define-key map "r" 'todo-raise-item)
419 (define-key map "s" 'todo-save)
f1757bdd 420 (define-key map "S" 'todo-save-top-priorities)
e4541b67 421 (define-key map "t" 'todo-top-priorities)
9278c60d
DL
422 map)
423 "TODO mode keymap.")
424
cb9222cf
DL
425(defvar todo-category-number 0 "TODO category number.")
426
427(defvar todo-tmp-buffer-name " *todo tmp*")
428
429(defvar todo-category-sep (make-string 75 ?-)
430 "Category separator.")
431
432(defvar todo-category-beg " --- "
433 "Category start separator to be prepended onto category name.")
434
435(defvar todo-category-end "--- End"
436 "Separator after a category.")
437
438(defvar todo-header "-*- mode: todo; "
439 "Header of todo files.")
440
441;; ---------------------------------------------------------------------------
3cb152f9 442
7f6241ea
OS
443(defun todo-category-select ()
444 "Make TODO mode display the current category correctly."
445 (let ((name (nth todo-category-number todo-categories)))
579e1c67 446 (setq mode-line-buffer-identification
cb9222cf 447;; (concat "Category: " name))
6fe3681b 448 (concat "Category: " (format "%18s" name)))
da2ee685
OS
449 (widen)
450 (goto-char (point-min))
7f6241ea 451 (search-forward-regexp
e4541b67
OS
452 (concat "^"
453 (regexp-quote (concat todo-prefix todo-category-beg name))
454 "$"))
9278c60d 455 (let ((begin (1+ (line-end-position))))
e4541b67 456 (search-forward-regexp (concat "^" todo-category-end))
9278c60d 457 (narrow-to-region begin (line-beginning-position))
7f6241ea
OS
458 (goto-char (point-min)))))
459(defalias 'todo-cat-slct 'todo-category-select)
460
9278c60d
DL
461(defun todo-forward-category ()
462 "Go forward to TODO list of next category."
da2ee685 463 (interactive)
7f6241ea 464 (setq todo-category-number
01b864bc 465 (mod (1+ todo-category-number) (length todo-categories)))
7f6241ea
OS
466 (todo-category-select))
467(defalias 'todo-cmd-forw 'todo-forward-category)
da2ee685 468
9278c60d
DL
469(defun todo-backward-category ()
470 "Go back to TODO list of previous category."
da2ee685 471 (interactive)
7f6241ea 472 (setq todo-category-number
01b864bc 473 (mod (1- todo-category-number) (length todo-categories)))
7f6241ea
OS
474 (todo-category-select))
475(defalias 'todo-cmd-back 'todo-backward-category)
da2ee685 476
9278c60d
DL
477(defun todo-backward-item ()
478 "Select previous entry of TODO list."
3cb152f9 479 (interactive)
7f6241ea 480 (search-backward-regexp (concat "^" (regexp-quote todo-prefix)) nil t)
cf1ebf43 481 (message ""))
7f6241ea 482(defalias 'todo-cmd-prev 'todo-backward-item)
3cb152f9 483
e4541b67 484(defun todo-forward-item (&optional count)
85b3b166 485 "Select COUNT-th next entry of TODO list."
e4541b67
OS
486 (interactive "P")
487 (if (listp count) (setq count (car count)))
7f6241ea 488 (end-of-line)
e4541b67
OS
489 (search-forward-regexp (concat "^" (regexp-quote todo-prefix))
490 nil 'goto-end count)
7f6241ea 491 (beginning-of-line)
cf1ebf43 492 (message ""))
7f6241ea 493(defalias 'todo-cmd-next 'todo-forward-item)
3cb152f9 494
cb9222cf
DL
495(defun todo-save ()
496 "Save the TODO list."
da2ee685 497 (interactive)
ade67f6a
RS
498 (save-excursion
499 (save-restriction
887c6c1f
RS
500 (save-buffer)))
501 (if todo-save-top-priorities-too (todo-save-top-priorities)))
7f6241ea 502(defalias 'todo-cmd-save 'todo-save)
da2ee685 503
cb9222cf
DL
504(defun todo-quit ()
505 "Done with TODO list for now."
3cb152f9 506 (interactive)
da2ee685 507 (widen)
f1757bdd 508 (todo-save)
7e6ed9b9 509 (message "")
cf1ebf43 510 (bury-buffer))
7f6241ea 511(defalias 'todo-cmd-done 'todo-quit)
3cb152f9 512
cb9222cf
DL
513(defun todo-edit-item ()
514 "Edit current TODO list entry."
3cb152f9 515 (interactive)
7f6241ea
OS
516 (let ((item (todo-item-string)))
517 (if (todo-string-multiline-p item)
01b864bc 518 (todo-edit-multiline)
7f6241ea 519 (let ((new (read-from-minibuffer "Edit: " item)))
01b864bc 520 (todo-remove-item)
cb9222cf 521 (insert new "\n")
01b864bc
OS
522 (todo-backward-item)
523 (message "")))))
7f6241ea
OS
524(defalias 'todo-cmd-edit 'todo-edit-item)
525
526(defun todo-edit-multiline ()
527 "Set up a buffer for editing a multiline TODO list entry."
528 (interactive)
529 (let ((buffer-name (generate-new-buffer-name todo-edit-buffer)))
e4541b67
OS
530 (switch-to-buffer
531 (make-indirect-buffer
cb9222cf 532 (file-name-nondirectory todo-file-do) buffer-name))
2186b974 533 (message "To exit, simply kill this buffer and return to list.")
7f6241ea
OS
534 (todo-edit-mode)
535 (narrow-to-region (todo-item-start) (todo-item-end))))
3cb152f9 536
ade0f0b8 537;;;###autoload
dad8ca4c 538(defun todo-add-category (cat)
85b3b166 539 "Add new category CAT to the TODO list."
6b04f517 540 (interactive "sCategory: ")
3cb152f9 541 (save-window-excursion
cb9222cf 542 (setq todo-categories (cons cat todo-categories))
3cb152f9 543 (find-file todo-file-do)
da2ee685
OS
544 (widen)
545 (goto-char (point-min))
546 (let ((posn (search-forward "-*- mode: todo; " 17 t)))
cb9222cf
DL
547 (if (not (null posn)) (goto-char posn))
548 (if (equal posn nil)
01b864bc 549 (progn
cb9222cf
DL
550 (insert "-*- mode: todo; \n")
551 (forward-char -1))
552 (kill-line)))
7f6241ea 553 (insert (format "todo-categories: %S; -*-" todo-categories))
cb9222cf 554 (forward-char 1)
e4541b67
OS
555 (insert (format "%s%s%s\n%s\n%s %s\n"
556 todo-prefix todo-category-beg cat
dad8ca4c 557 todo-category-end
cb9222cf 558 todo-prefix todo-category-sep)))
cf1ebf43 559 0)
da2ee685 560
ade0f0b8 561;;;###autoload
4dc1a160 562(defun todo-add-item-non-interactively (new-item category)
85b3b166 563 "Insert NEW-ITEM in TODO list as a new entry in CATEGORY."
4cbc9d5a 564 (save-excursion
49b2ae0e
OS
565 (todo-show))
566 (save-excursion
e4541b67
OS
567 (if (string= "" category)
568 (setq category (nth todo-category-number todo-categories)))
cb9222cf
DL
569 (let ((cat-exists (member category todo-categories)))
570 (setq todo-category-number
571 (if cat-exists
572 (- (length todo-categories) (length cat-exists))
573 (todo-add-category category))))
4dc1a160
OS
574 (todo-show)
575 (setq todo-previous-line 0)
576 (let ((top 1)
577 (bottom (1+ (count-lines (point-min) (point-max)))))
578 (while (> (- bottom top) todo-insert-threshold)
579 (let* ((current (/ (+ top bottom) 2))
580 (answer (if (< current bottom)
cb9222cf 581 (todo-more-important-p current) nil)))
4dc1a160
OS
582 (if answer
583 (setq bottom current)
584 (setq top (1+ current)))))
585 (setq top (/ (+ top bottom) 2))
586 ;; goto-line doesn't have the desired behavior in a narrowed buffer
587 (goto-char (point-min))
588 (forward-line (1- top)))
cb9222cf 589 (insert new-item "\n")
7f6241ea 590 (todo-backward-item)
f1757bdd 591 (todo-save)
3d8105fb 592 (message "")))
4cbc9d5a 593
ade0f0b8 594;;;###autoload
89802f43 595(defun todo-insert-item (arg)
85b3b166
OS
596 "Insert new TODO list entry.
597With a prefix argument solicit the category, otherwise use the current
598category."
4cbc9d5a 599 (interactive "P")
49b2ae0e 600 (save-excursion
1966902e
OS
601 (if (not (string-equal mode-name "TODO")) (todo-show))
602 (let* ((new-item (concat todo-prefix " "
603 (read-from-minibuffer
604 "New TODO entry: "
605 (if todo-entry-prefix-function
606 (funcall todo-entry-prefix-function)))))
607 (categories todo-categories)
608 (history (cons 'categories (1+ todo-category-number)))
609 (current-category (nth todo-category-number todo-categories))
dad8ca4c 610 (category
89802f43 611 (if arg
1966902e 612 current-category
cb9222cf
DL
613 (completing-read (concat "Category [" current-category "]: ")
614 (todo-category-alist) nil nil nil
615 history current-category))))
4dc1a160 616 (todo-add-item-non-interactively new-item category))))
4cbc9d5a 617
7f6241ea 618(defalias 'todo-cmd-inst 'todo-insert-item)
3d8105fb 619
d145aa83
OS
620(defun todo-insert-item-here ()
621 "Insert new TODO list entry under the cursor."
4dc1a160
OS
622 (interactive "")
623 (save-excursion
624 (if (not (string-equal mode-name "TODO")) (todo-show))
625 (let* ((new-item (concat todo-prefix " "
626 (read-from-minibuffer
627 "New TODO entry: "
628 (if todo-entry-prefix-function
629 (funcall todo-entry-prefix-function))))))
630 (insert (concat new-item "\n")))))
d145aa83 631
01b864bc 632(defun todo-more-important-p (line)
7f6241ea
OS
633 "Ask whether entry is more important than the one at LINE."
634 (if (not (equal todo-previous-line line))
3d8105fb 635 (progn
7f6241ea 636 (setq todo-previous-line line)
3d8105fb 637 (goto-char (point-min))
7f6241ea 638 (forward-line (1- todo-previous-line))
01b864bc
OS
639 (let ((item (todo-item-string-start)))
640 (setq todo-previous-answer
641 (y-or-n-p (concat "More important than '" item "'? "))))))
7f6241ea
OS
642 todo-previous-answer)
643(defalias 'todo-ask-p 'todo-more-important-p)
644
cb9222cf
DL
645(defun todo-delete-item ()
646 "Delete current TODO list entry."
3cb152f9
OS
647 (interactive)
648 (if (> (count-lines (point-min) (point-max)) 0)
7f6241ea 649 (let* ((todo-entry (todo-item-string-start))
01b864bc
OS
650 (todo-answer (y-or-n-p (concat "Permanently remove '"
651 todo-entry "'? "))))
cb9222cf
DL
652 (if todo-answer
653 (progn
654 (todo-remove-item)
655 (todo-backward-item)))
01b864bc 656 (message ""))
7f6241ea
OS
657 (error "No TODO list entry to delete")))
658(defalias 'todo-cmd-kill 'todo-delete-item)
3cb152f9 659
cb9222cf
DL
660(defun todo-raise-item ()
661 "Raise priority of current entry."
8cdc3b3d 662 (interactive)
7f6241ea
OS
663 (if (> (count-lines (point-min) (point)) 0)
664 (let ((item (todo-item-string)))
01b864bc
OS
665 (todo-remove-item)
666 (todo-backward-item)
667 (save-excursion
cb9222cf 668 (insert item "\n"))
01b864bc 669 (message ""))
7f6241ea 670 (error "No TODO list entry to raise")))
cb9222cf 671(defalias 'todo-cmd-rais 'todo-raise-item)
8cdc3b3d 672
cb9222cf
DL
673(defun todo-lower-item ()
674 "Lower priority of current entry."
8cdc3b3d 675 (interactive)
e4541b67
OS
676 (if (> (count-lines (point) (point-max)) 1)
677 ;; Assume there is a final newline
7f6241ea 678 (let ((item (todo-item-string)))
01b864bc
OS
679 (todo-remove-item)
680 (todo-forward-item)
681 (save-excursion
cb9222cf 682 (insert item "\n"))
01b864bc 683 (message ""))
7f6241ea
OS
684 (error "No TODO list entry to lower")))
685(defalias 'todo-cmd-lowr 'todo-lower-item)
8cdc3b3d 686
0561decb 687(defun todo-file-item (&optional comment)
a554b301 688 "File the current TODO list entry away, annotated with an optional COMMENT."
0561decb
OS
689 (interactive "sComment: ")
690 (or (> (count-lines (point-min) (point-max)) 0)
691 (error "No TODO list entry to file away"))
692 (let ((time-stamp-format todo-time-string-format))
cb9222cf
DL
693 (if (and comment (> (length comment) 0))
694 (progn
695 (goto-char (todo-item-end))
696 (insert
697 (if (save-excursion (beginning-of-line)
698 (looking-at (regexp-quote todo-prefix)))
699 " "
700 "\n\t")
701 "(" comment ")")))
0561decb
OS
702 (goto-char (todo-item-end))
703 (insert " [" (nth todo-category-number todo-categories) "]")
704 (goto-char (todo-item-start))
705 (let ((temp-point (point)))
706 (if (looking-at (regexp-quote todo-prefix))
707 (replace-match (time-stamp-string))
708 ;; Standard prefix -> timestamp
709 ;; Else prefix non-standard item start with timestamp
710 (insert (time-stamp-string)))
711 (append-to-file temp-point (1+ (todo-item-end)) todo-file-done)
712 (delete-region temp-point (1+ (todo-item-end))))
713 (todo-backward-item)
714 (message "")))
3cb152f9
OS
715
716;; ---------------------------------------------------------------------------
717
7f6241ea
OS
718;; Utility functions:
719
cb9222cf
DL
720
721;;;###autoload
e4541b67
OS
722(defun todo-top-priorities (&optional nof-priorities category-pr-page)
723 "List top priorities for each category.
724
725Number of entries for each category is given by NOF-PRIORITIES which
726defaults to \'todo-show-priorities\'.
727
728If CATEGORY-PR-PAGE is non-nil, a page separator \'^L\' is inserted
729between each category."
730
731 (interactive "P")
732 (or nof-priorities (setq nof-priorities todo-show-priorities))
733 (if (listp nof-priorities) ;universal argument
734 (setq nof-priorities (car nof-priorities)))
a54d1c94 735 (let ((todo-print-buffer-name todo-tmp-buffer-name)
85b3b166
OS
736 ;;(todo-print-category-number 0)
737 (todo-category-break (if category-pr-page "\f" ""))
e4541b67
OS
738 (cat-end
739 (concat
740 (if todo-remove-separator
741 (concat todo-category-end "\n"
742 (regexp-quote todo-prefix) " " todo-category-sep "\n")
743 (concat todo-category-end "\n"))))
744 beg end)
745 (todo-show)
746 (save-excursion
747 (save-restriction
748 (widen)
85b3b166
OS
749 (copy-to-buffer todo-print-buffer-name (point-min) (point-max))
750 (set-buffer todo-print-buffer-name)
e4541b67 751 (goto-char (point-min))
bf2fcd67
GM
752 (when (re-search-forward (regexp-quote todo-header) nil t)
753 (beginning-of-line 1)
754 (delete-region (point) (line-end-position)))
e4541b67
OS
755 (while (re-search-forward ;Find category start
756 (regexp-quote (concat todo-prefix todo-category-beg))
757 nil t)
9278c60d 758 (setq beg (+ (line-end-position) 1)) ;Start of first entry.
e4541b67
OS
759 (re-search-forward cat-end nil t)
760 (setq end (match-beginning 0))
761 (replace-match todo-category-break)
762 (narrow-to-region beg end) ;In case we have too few entries.
763 (goto-char (point-min))
764 (if (= 0 nof-priorities) ;Traverse entries.
cb9222cf 765 (goto-char end) ;All entries
e4541b67
OS
766 (todo-forward-item nof-priorities))
767 (setq beg (point))
768 (delete-region beg end)
769 (widen))
f1757bdd 770 (and (looking-at "\f") (replace-match "")) ;Remove trailing form-feed.
e4541b67
OS
771 (goto-char (point-min)) ;Due to display buffer
772 ))
773 ;; Could have used switch-to-buffer as it has a norecord argument,
774 ;; which is nice when we are called from e.g. todo-print.
f1757bdd 775 ;; Else we could have used pop-to-buffer.
85b3b166 776 (display-buffer todo-print-buffer-name)
e4541b67 777 (message "Type C-x 1 to remove %s window. M-C-v to scroll the help."
9278c60d 778 todo-print-buffer-name)))
e4541b67 779
f1757bdd
OS
780(defun todo-save-top-priorities (&optional nof-priorities)
781 "Save top priorities for each category in `todo-file-top'.
782
783Number of entries for each category is given by NOF-PRIORITIES which
784defaults to `todo-show-priorities'."
785 (interactive "P")
cb9222cf
DL
786 (save-window-excursion
787 (save-excursion
788 (save-restriction
789 (todo-top-priorities nof-priorities)
790 (set-buffer todo-tmp-buffer-name)
791 (write-file todo-file-top)
792 (kill-this-buffer)))))
f1757bdd 793
e4541b67
OS
794;;;###autoload
795(defun todo-print (&optional category-pr-page)
a554b301 796 "Print todo summary using `todo-print-function'.
cb9222cf 797If CATEGORY-PR-PAGE is non-nil, a page separator `^L' is inserted
e4541b67
OS
798between each category.
799
cb9222cf 800Number of entries for each category is given by `todo-print-priorities'."
e4541b67 801 (interactive "P")
cb9222cf
DL
802 (save-window-excursion
803 (save-excursion
804 (save-restriction
805 (todo-top-priorities todo-print-priorities
9278c60d 806 category-pr-page)
cb9222cf
DL
807 (set-buffer todo-tmp-buffer-name)
808 (and (funcall todo-print-function)
809 (kill-this-buffer))
810 (message "Todo printing done.")))))
e4541b67
OS
811
812(defun todo-jump-to-category ()
813 "Jump to a category. Default is previous category."
814 (interactive)
815 (let* ((categories todo-categories)
cb9222cf
DL
816 (history (cons 'categories (1+ todo-category-number)))
817 (default (nth todo-category-number todo-categories))
dad8ca4c 818 (category (completing-read
cb9222cf
DL
819 (concat "Category [" default "]: ")
820 (todo-category-alist) nil nil nil history default)))
e4541b67
OS
821 (if (string= "" category)
822 (setq category (nth todo-category-number todo-categories)))
823 (setq todo-category-number
cb9222cf
DL
824 (if (member category todo-categories)
825 (- (length todo-categories)
826 (length (member category todo-categories)))
827 (todo-add-category category)))
e4541b67
OS
828 (todo-show)))
829
9278c60d
DL
830(defun todo-line-string ()
831 "Return current line in buffer as a string."
832 (buffer-substring (line-beginning-position) (line-end-position)))
7f6241ea
OS
833
834(defun todo-item-string-start ()
835 "Return the start of this TODO list entry as a string."
836 ;; Suitable for putting in the minibuffer when asking the user
837 (let ((item (todo-item-string)))
838 (if (> (length item) 60)
01b864bc 839 (setq item (concat (substring item 0 56) "...")))
7f6241ea
OS
840 item))
841
9278c60d
DL
842(defun todo-item-start ()
843 "Return point at start of current TODO list item."
7f6241ea
OS
844 (save-excursion
845 (beginning-of-line)
846 (if (not (looking-at (regexp-quote todo-prefix)))
01b864bc
OS
847 (search-backward-regexp
848 (concat "^" (regexp-quote todo-prefix)) nil t))
7f6241ea
OS
849 (point)))
850
9278c60d
DL
851(defun todo-item-end ()
852 "Return point at end of current TODO list item."
7f6241ea
OS
853 (save-excursion
854 (end-of-line)
e4541b67
OS
855 (search-forward-regexp
856 (concat "^" (regexp-quote todo-prefix)) nil 'goto-end)
9278c60d 857 (1- (line-beginning-position))))
7f6241ea 858
9278c60d
DL
859(defun todo-remove-item ()
860 "Delete the current entry from the TODO list."
7f6241ea
OS
861 (delete-region (todo-item-start) (1+ (todo-item-end))))
862
cb9222cf
DL
863(defun todo-item-string ()
864 "Return current TODO list entry as a string."
7f6241ea
OS
865 (buffer-substring (todo-item-start) (todo-item-end)))
866
867(defun todo-string-count-lines (string)
868 "Return the number of lines STRING spans."
869 (length (split-string string "\n")))
870
871(defun todo-string-multiline-p (string)
85b3b166 872 "Return non-nil if STRING spans several lines."
7f6241ea
OS
873 (> (todo-string-count-lines string) 1))
874
875(defun todo-category-alist ()
85b3b166 876 "Generate an alist for use in `completing-read' from `todo-categories'."
a554b301 877 (mapcar #'list todo-categories))
7f6241ea 878
da2ee685
OS
879;; ---------------------------------------------------------------------------
880
cb9222cf
DL
881(easy-menu-define todo-menu todo-mode-map "Todo Menu"
882 '("Todo"
883 ["Next category" todo-forward-category t]
884 ["Previous category" todo-backward-category t]
885 ["Jump to category" todo-jump-to-category t]
886 ["Show top priority items" todo-top-priorities t]
887 ["Print categories" todo-print t]
888 "---"
889 ["Edit item" todo-edit-item t]
890 ["File item" todo-file-item t]
891 ["Insert new item" todo-insert-item t]
892 ["Insert item here" todo-insert-item-here t]
893 ["Kill item" todo-delete-item t]
894 "---"
895 ["Lower item priority" todo-lower-item t]
896 ["Raise item priority" todo-raise-item t]
897 "---"
898 ["Next item" todo-forward-item t]
899 ["Previous item" todo-backward-item t]
900 "---"
901 ["Save" todo-save t]
902 ["Save Top Priorities" todo-save-top-priorities t]
903 "---"
904 ["Quit" todo-quit t]
905 ))
a360be79 906
e4541b67 907;; As calendar reads .todo-do before todo-mode is loaded.
ade0f0b8 908;;;###autoload
a554b301
DL
909(defun todo-mode ()
910 "Major mode for editing TODO lists.
911
912\\{todo-mode-map}"
3cb152f9 913 (interactive)
3cb152f9
OS
914 (setq major-mode 'todo-mode)
915 (setq mode-name "TODO")
916 (use-local-map todo-mode-map)
49c48a1b
OS
917 (easy-menu-add todo-menu)
918 (run-hooks 'todo-mode-hook))
da2ee685 919
cb9222cf
DL
920(eval-when-compile
921 (defvar date)
922 (defvar entry))
923
5d035cad 924;; Read about this function in the setup instructions above!
ade0f0b8 925;;;###autoload
5d035cad 926(defun todo-cp ()
a554b301 927 "Make a diary entry appear only in the current date's diary."
5d035cad 928 (if (equal (calendar-current-date) date)
a554b301 929 entry))
5d035cad 930
cb9222cf 931(define-derived-mode todo-edit-mode text-mode "TODO Edit"
a554b301
DL
932 "Major mode for editing items in the TODO list.
933
cb9222cf 934\\{todo-edit-mode-map}")
7f6241ea 935
ade0f0b8 936;;;###autoload
a554b301
DL
937(defun todo-show ()
938 "Show TODO list."
da2ee685 939 (interactive)
7f6241ea
OS
940 (if (file-exists-p todo-file-do)
941 (find-file todo-file-do)
942 (todo-initial-setup))
cb9222cf
DL
943 (if (null todo-categories)
944 (if (null todo-cats)
945 (error "Error in %s: No categories in list `todo-categories'"
946 todo-file-do)
947 (goto-char (point-min))
948 (and (search-forward "todo-cats:" nil t)
949 (replace-match "todo-categories:"))
950 (make-local-variable 'todo-categories)
951 (setq todo-categories todo-cats)))
7f6241ea
OS
952 (beginning-of-line)
953 (todo-category-select))
954
a554b301
DL
955(defun todo-initial-setup ()
956 "Set up things to work properly in TODO mode."
da2ee685 957 (find-file todo-file-do)
7f6241ea
OS
958 (erase-buffer)
959 (todo-mode)
960 (todo-add-category "Todo"))
3cb152f9 961
7c896f63 962(provide 'todo-mode)
3cb152f9 963
7c896f63 964;;; todo-mode.el ends here