comparison lisp/calendar/todo-mode.el @ 30028:2a636f84f3c5

Replaced with a working version, based on revision 1.34 tidied up.
author Dave Love <fx@gnu.org>
date Tue, 04 Jul 2000 11:15:24 +0000
parents a37837ad882e
children 152ae6b57597
comparison
equal deleted inserted replaced
30027:a37837ad882e 30028:2a636f84f3c5
1 ;; todo-mode.el -- Major mode for editing TODO list files 1 ;; todo-mode.el -- Major mode for editing TODO list files
2 2
3 ;; Copyright (C) 1997, 2000 Free Software Foundation, Inc. 3 ;; Copyright (C) 1997, 1999 Free Software Foundation, Inc.
4 4
5 ;; Author: os10000@seidel-space.de [not clear that this works, July 2000] 5 ;; Author: Oliver Seidel <os10000@seidel-space.de>
6 ;; [Not clear the above works, July 2000]
6 ;; Created: 2 Aug 1997 7 ;; Created: 2 Aug 1997
7 ;; Version: $Id: todo-mode.el,v 1.41 2000/06/06 16:43:40 fx Exp $ 8 ;; Version: $Id: todo-mode.el,v 1.1 1999/05/12 11:49:30 fx Exp fx $
8 ;; Keywords: Categorised TODO list editor, todo-mode 9 ;; Keywords: calendar, todo
9 10
10 ;; This file is part of GNU Emacs. 11 ;; This file is part of GNU Emacs.
11 12
12 ;; GNU Emacs is free software; you can redistribute it and/or modify 13 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by 14 ;; it under the terms of the GNU General Public License as published by
58 ;; (autoload 'todo-insert-item "todo-mode" 59 ;; (autoload 'todo-insert-item "todo-mode"
59 ;; "Add TODO item." t) 60 ;; "Add TODO item." t)
60 ;; 61 ;;
61 ;; You may now enter new items by typing "M-x todo-insert-item", 62 ;; You may now enter new items by typing "M-x todo-insert-item",
62 ;; or enter your TODO list file by typing "M-x todo-show". 63 ;; or enter your TODO list file by typing "M-x todo-show".
63 ;;
64 ;; -------------------------------------------------------------
65 ;;
66 ;; oh no, it doesn't work any more ... but Alex Schroeder
67 ;; <a.schroeder@bsiag.ch> writes:
68 ;;
69 ;; -------------------------------------------------------------
70 ;;
71 ;; 1. Call todo-show (I called todo-insert first)
72 ;; 2. Add some categories (I called todo-insert)
73 ;; 3. Save the buffer, restart Emacs (perhaps restarting Emacs is not
74 ;; required)
75 ;; 4. Only now can you start to add entries.
76 ;;
77 ;; This is a bit cumbersome, and it should be documented. You probably
78 ;;
79 ;; -------------------------------------------------------------
80 ;;
81 ;; and right he is. My apologies, I'll try to fix it sometime.
82 ;;
83 ;; -------------------------------------------------------------
84 ;; 64 ;;
85 ;; The TODO list file has a special format and some auxiliary 65 ;; The TODO list file has a special format and some auxiliary
86 ;; information, which will be added by the todo-show function if 66 ;; information, which will be added by the todo-show function if
87 ;; it attempts to visit an un-initialised file. Hence it is 67 ;; it attempts to visit an un-initialised file. Hence it is
88 ;; recommended to use the todo-show function for the first time, 68 ;; recommended to use the todo-show function for the first time,
115 ;; 95 ;;
116 ;; Version 96 ;; Version
117 ;; 97 ;;
118 ;; Which version of todo-mode.el does this documentation refer to? 98 ;; Which version of todo-mode.el does this documentation refer to?
119 ;; 99 ;;
120 ;; $Id: todo-mode.el,v 1.41 2000/06/06 16:43:40 fx Exp $ 100 ;; $Id: todo-mode.el,v 1.1 1999/05/12 11:49:30 fx Exp fx $
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
121 ;; 109 ;;
122 ;; Operation 110 ;; Operation
123 ;; 111 ;;
124 ;; You will have the following facilities available: 112 ;; You will have the following facilities available:
125 ;; 113 ;;
126 ;; M-x todo-show will enter the todo list screen, here type 114 ;; M-x todo-show will enter the todo list screen, here type
127 ;;
128 ;; spc will toggle the display of sub-trees
129 ;; 115 ;;
130 ;; + to go to next category 116 ;; + to go to next category
131 ;; - to go to previous category 117 ;; - to go to previous category
132 ;; d to file the current entry, including a 118 ;; d to file the current entry, including a
133 ;; comment and timestamp 119 ;; comment and timestamp
276 ;; Oliver Seidel 262 ;; Oliver Seidel
277 ;; (Lessingstr. 8, 65760 Eschborn, Federal Republic of Germany) 263 ;; (Lessingstr. 8, 65760 Eschborn, Federal Republic of Germany)
278 264
279 ;;; Code: 265 ;;; Code:
280 266
281 (eval-when-compile
282 (require 'outline)
283 (require 'calendar)
284 ;; Calendar dynamic bondage:
285 (defvar date)
286 (defvar entry))
287 (autoload 'time-stamp-string "time-stamp")
288
289 ;; User-configurable variables: 267 ;; User-configurable variables:
290 268
291 (defgroup todo nil 269 (defgroup todo nil
292 "Maintain a list of todo items." 270 "Maintain a list of todo items."
293 :version "21.1"
294 :group 'calendar) 271 :group 'calendar)
295 272
296 (defcustom todo-prefix "*/*" 273 (defcustom todo-prefix "*/*"
297 "*TODO mode prefix for entries. 274 "*TODO mode prefix for entries.
298 275
299 This is useful in conjunction with `calendar' and `diary' if you use 276 This is useful in conjunction with `calendar' and `diary' if you use
300 277
301 #include \"~/.todo-do\" 278 #include \"~/.todo-do\"
306 Using \"&%%(equal (calendar-current-date) date)\" instead will only 283 Using \"&%%(equal (calendar-current-date) date)\" instead will only
307 show and mark todo entreis for today, but may slow down processing of 284 show and mark todo entreis for today, but may slow down processing of
308 the diary file somewhat." 285 the diary file somewhat."
309 :type 'string 286 :type 'string
310 :group 'todo) 287 :group 'todo)
311 (defcustom todo-file-do "~/.todo-do" 288 (defcustom todo-file-do "~/.todo-do"
312 "*TODO mode list file." 289 "*TODO mode list file."
313 :type 'file 290 :type 'file
314 :group 'todo) 291 :group 'todo)
315 (defcustom todo-file-done "~/.todo-done" 292 (defcustom todo-file-done "~/.todo-done"
316 "*TODO mode archive file." 293 "*TODO mode archive file."
317 :type 'file 294 :type 'file
318 :group 'todo) 295 :group 'todo)
319 (defcustom todo-mode-hook nil 296 (defcustom todo-mode-hook nil
320 "*TODO mode hooks." 297 "*TODO mode hooks."
338 e.g. 8, it will stop as soon as the window size drops below that 315 e.g. 8, it will stop as soon as the window size drops below that
339 amount and will insert the item in the approximate centre of that 316 amount and will insert the item in the approximate centre of that
340 window." 317 window."
341 :type 'integer 318 :type 'integer
342 :group 'todo) 319 :group 'todo)
343 (defvar todo-edit-buffer " *TODO Edit*" "TODO Edit buffer name.") 320 (defvar todo-edit-buffer " *TODO Edit*"
321 "TODO Edit buffer name.")
344 (defcustom todo-file-top "~/.todo-top" 322 (defcustom todo-file-top "~/.todo-top"
345 "*TODO mode top priorities file. 323 "*TODO mode top priorities file.
346 324
347 Not in TODO format, but diary compatible. 325 Not in TODO format, but diary compatible.
348 Automatically generated when `todo-save-top-priorities' is non-nil." 326 Automatically generated when `todo-save-top-priorities' is non-nil."
362 "*Default number of priorities to print by \\[todo-print]. 340 "*Default number of priorities to print by \\[todo-print].
363 0 means print all entries." 341 0 means print all entries."
364 :type 'integer 342 :type 'integer
365 :group 'todo) 343 :group 'todo)
366 (defcustom todo-remove-separator t 344 (defcustom todo-remove-separator t
367 "*Non-nil to remove category separators in \ 345 "*Non-nil to remove category separators in\
368 \\[todo-top-priorities] and \\[todo-print]." 346 \\[todo-top-priorities] and \\[todo-print]."
369 :type 'boolean 347 :type 'boolean
370 :group 'todo) 348 :group 'todo)
371 (defcustom todo-save-top-priorities-too t 349 (defcustom todo-save-top-priorities-too t
372 "*Non-nil makes `todo-save' automatically save top-priorities in `todo-file-top'." 350 "*Non-nil makes `todo-save' automatically save top-priorities in `todo-file-top'."
375 353
376 ;; Thanks for the ISO time stamp format go to Karl Eichwalder <ke@suse.de> 354 ;; Thanks for the ISO time stamp format go to Karl Eichwalder <ke@suse.de>
377 ;; My format string for the appt.el package is "%3b %2d, %y, %02I:%02M%p". 355 ;; My format string for the appt.el package is "%3b %2d, %y, %02I:%02M%p".
378 ;; 356 ;;
379 (defcustom todo-time-string-format 357 (defcustom todo-time-string-format
380 "%04y-%02m-%02d %02H:%02M" 358 "%:y-%02m-%02d %02H:%02M"
381 "*TODO mode time string format for done entries. 359 "*TODO mode time string format for done entries.
382 For details see the variable `time-stamp-format'." 360 For details see the variable `time-stamp-format'."
383 :type 'string 361 :type 'string
384 :group 'todo) 362 :group 'todo)
385 363
389 :group 'todo) 367 :group 'todo)
390 (defcustom todo-initials (or (getenv "INITIALS") (user-login-name)) 368 (defcustom todo-initials (or (getenv "INITIALS") (user-login-name))
391 "*Initials of todo item author." 369 "*Initials of todo item author."
392 :type 'string 370 :type 'string
393 :group 'todo) 371 :group 'todo)
372
373 (autoload 'time-stamp-string "time-stamp")
394 374
395 (defun todo-entry-timestamp-initials () 375 (defun todo-entry-timestamp-initials ()
396 "Prepend timestamp and your initials to the head of a TODO entry." 376 "Prepend timestamp and your initials to the head of a TODO entry."
397 (let ((time-stamp-format todo-time-string-format)) 377 (let ((time-stamp-format todo-time-string-format))
398 (concat (time-stamp-string) " " todo-initials ": "))) 378 (concat (time-stamp-string) " " todo-initials ": ")))
399 379
380 ;; ---------------------------------------------------------------------------
381
400 ;; Set up some helpful context ... 382 ;; Set up some helpful context ...
401 383
402 (defvar todo-categories nil 384 (defvar todo-categories nil
403 "TODO categories.") 385 "TODO categories.")
386
404 (defvar todo-cats nil 387 (defvar todo-cats nil
405 "Old variable for holding the TODO categories. 388 "Old variable for holding the TODO categories.
406 Use `todo-categories' instead.") 389 Use `todo-categories' instead.")
407 (defvar todo-previous-line 0 390
391 (defvar todo-previous-line 0
408 "Previous line asked about.") 392 "Previous line asked about.")
409 (defvar todo-previous-answer 0 393
394 (defvar todo-previous-answer 0
410 "Previous answer got.") 395 "Previous answer got.")
411 (defvar todo-category-number 0
412 "TODO category number.")
413
414 (defvar todo-category-sep (make-string 75 ?-)
415 "Category separator.")
416 (defvar todo-category-beg " --- "
417 "Category start separator to be prepended onto category name.")
418 (defvar todo-category-end "--- End"
419 "Separator after a category.")
420 (defvar todo-header "-*- mode: todo; "
421 "Header of todo files.")
422
423 ;; ---------------------------------------------------------------------------
424 396
425 (defvar todo-mode-map 397 (defvar todo-mode-map
426 (let ((map (make-keymap))) 398 (let ((map (make-keymap)))
427 (suppress-keymap map t) 399 (suppress-keymap map t)
428 (define-key map "?" 'todo-help)
429 (define-key map " " 'todo-hide-show-subtree)
430 (define-key map "+" 'todo-forward-category) 400 (define-key map "+" 'todo-forward-category)
431 (define-key map "-" 'todo-backward-category) 401 (define-key map "-" 'todo-backward-category)
432 (define-key map "d" 'todo-file-item) ;done/delete 402 (define-key map "d" 'todo-file-item) ;done/delete
433 (define-key map "e" 'todo-edit-item) 403 (define-key map "e" 'todo-edit-item)
434 (define-key map "E" 'todo-edit-multiline) 404 (define-key map "E" 'todo-edit-multiline)
447 (define-key map "S" 'todo-save-top-priorities) 417 (define-key map "S" 'todo-save-top-priorities)
448 (define-key map "t" 'todo-top-priorities) 418 (define-key map "t" 'todo-top-priorities)
449 map) 419 map)
450 "TODO mode keymap.") 420 "TODO mode keymap.")
451 421
452 (defun todo-position (item list) 422 (defvar todo-category-number 0 "TODO category number.")
453 "Return the position of the element in LIST testing `equal' to ITEM. 423
454 Return nil if ITEM not found." 424 (defvar todo-tmp-buffer-name " *todo tmp*")
455 (let ((pos 0) 425
456 found) 426 (defvar todo-category-sep (make-string 75 ?-)
457 (while list 427 "Category separator.")
458 (if (equal item (pop list)) 428
459 (setq list nil 429 (defvar todo-category-beg " --- "
460 found pos) 430 "Category start separator to be prepended onto category name.")
461 (setq pos (1+ pos)))) 431
462 found)) 432 (defvar todo-category-end "--- End"
433 "Separator after a category.")
434
435 (defvar todo-header "-*- mode: todo; "
436 "Header of todo files.")
437
438 ;; ---------------------------------------------------------------------------
463 439
464 (defun todo-category-select () 440 (defun todo-category-select ()
465 "Make TODO mode display the current category correctly." 441 "Make TODO mode display the current category correctly."
466 (let ((name (nth todo-category-number todo-categories))) 442 (let ((name (nth todo-category-number todo-categories)))
467 (setq mode-line-buffer-identification 443 (setq mode-line-buffer-identification
444 ;; (concat "Category: " name))
468 (concat "Category: " (format "%18s" name))) 445 (concat "Category: " (format "%18s" name)))
469 (widen) 446 (widen)
470 (goto-char (point-min)) 447 (goto-char (point-min))
471 (search-forward-regexp 448 (search-forward-regexp
472 (concat "^" 449 (concat "^"
476 (search-forward-regexp (concat "^" todo-category-end)) 453 (search-forward-regexp (concat "^" todo-category-end))
477 (narrow-to-region begin (line-beginning-position)) 454 (narrow-to-region begin (line-beginning-position))
478 (goto-char (point-min))))) 455 (goto-char (point-min)))))
479 (defalias 'todo-cat-slct 'todo-category-select) 456 (defalias 'todo-cat-slct 'todo-category-select)
480 457
481 (defun todo-help () "Show TODO mode help."
482 (interactive)
483 (describe-function 'todo-mode))
484
485 (defun todo-hide-show-subtree ()
486 "Hide or Show subtrees in the TODO list."
487 (interactive)
488 (save-excursion
489 (end-of-line)
490 (if (outline-visible)
491 (hide-subtree)
492 (show-subtree))))
493
494 (defun todo-forward-category () 458 (defun todo-forward-category ()
495 "Go forward to TODO list of next category." 459 "Go forward to TODO list of next category."
496 (interactive) 460 (interactive)
497 (setq todo-category-number 461 (setq todo-category-number
498 (mod (1+ todo-category-number) (length todo-categories))) 462 (mod (1+ todo-category-number) (length todo-categories)))
523 nil 'goto-end count) 487 nil 'goto-end count)
524 (beginning-of-line) 488 (beginning-of-line)
525 (message "")) 489 (message ""))
526 (defalias 'todo-cmd-next 'todo-forward-item) 490 (defalias 'todo-cmd-next 'todo-forward-item)
527 491
528 (defun todo-save () "Save the TODO list." 492 (defun todo-save ()
493 "Save the TODO list."
529 (interactive) 494 (interactive)
530 (save-buffer) 495 (save-buffer)
531 (if todo-save-top-priorities-too (todo-save-top-priorities))) 496 (if todo-save-top-priorities-too (todo-save-top-priorities))
497 )
532 (defalias 'todo-cmd-save 'todo-save) 498 (defalias 'todo-cmd-save 'todo-save)
533 499
534 (defun todo-quit () "Done with TODO list for now." 500 (defun todo-quit ()
501 "Done with TODO list for now."
535 (interactive) 502 (interactive)
536 (widen) 503 (widen)
537 (todo-save) 504 (todo-save)
538 (message "") 505 (message "")
539 (bury-buffer)) 506 (bury-buffer))
540 (defalias 'todo-cmd-done 'todo-quit) 507 (defalias 'todo-cmd-done 'todo-quit)
541 508
542 (defun todo-edit-item () "Edit current TODO list entry." 509 (defun todo-edit-item ()
510 "Edit current TODO list entry."
543 (interactive) 511 (interactive)
544 (let ((item (todo-item-string))) 512 (let ((item (todo-item-string)))
545 (if (todo-string-multiline-p item) 513 (if (todo-string-multiline-p item)
546 (todo-edit-multiline) 514 (todo-edit-multiline)
547 (let ((new (read-from-minibuffer "Edit: " item))) 515 (let ((new (read-from-minibuffer "Edit: " item)))
548 (todo-remove-item) 516 (todo-remove-item)
549 (insert new ?\n) 517 (insert new "\n")
550 (todo-backward-item) 518 (todo-backward-item)
551 (message ""))))) 519 (message "")))))
552 (defalias 'todo-cmd-edit 'todo-edit-item) 520 (defalias 'todo-cmd-edit 'todo-edit-item)
553 521
554 (defun todo-edit-multiline () 522 (defun todo-edit-multiline ()
555 "Set up a buffer for editing a multiline TODO list entry." 523 "Set up a buffer for editing a multiline TODO list entry."
556 (interactive) 524 (interactive)
557 (let ((buffer-name (generate-new-buffer-name todo-edit-buffer))) 525 (let ((buffer-name (generate-new-buffer-name todo-edit-buffer)))
558 (switch-to-buffer 526 (switch-to-buffer
559 (make-indirect-buffer 527 (make-indirect-buffer
560 (find-buffer-visiting todo-file-do) buffer-name)) 528 (file-name-nondirectory todo-file-do) buffer-name))
561 (message "To exit, simply kill this buffer and return to list.") 529 (message "To exit, simply kill this buffer and return to list.")
562 (todo-edit-mode) 530 (todo-edit-mode)
563 (narrow-to-region (todo-item-start) (todo-item-end)))) 531 (narrow-to-region (todo-item-start) (todo-item-end))))
564 532
533 ;;;### autoload
565 (defun todo-add-category (cat) 534 (defun todo-add-category (cat)
566 "Add new category CAT to the TODO list." 535 "Add new category CAT to the TODO list."
567 (interactive "sCategory: ") 536 (interactive "sCategory: ")
568 (save-window-excursion 537 (save-window-excursion
569 (add-to-list 'todo-categories cat) 538 (setq todo-categories (cons cat todo-categories))
570 (find-file todo-file-do) 539 (find-file todo-file-do)
571 (widen) 540 (widen)
572 (goto-char (point-min)) 541 (goto-char (point-min))
573 (let ((posn (search-forward "-*- mode: todo; " 17 t))) 542 (let ((posn (search-forward "-*- mode: todo; " 17 t)))
574 (if posn 543 (if (not (null posn)) (goto-char posn))
544 (if (equal posn nil)
575 (progn 545 (progn
576 (goto-char posn) 546 (insert "-*- mode: todo; \n")
577 (kill-line)) 547 (forward-char -1))
578 (insert "-*- mode: todo; \n") 548 (kill-line)))
579 (backward-char)))
580 (insert (format "todo-categories: %S; -*-" todo-categories)) 549 (insert (format "todo-categories: %S; -*-" todo-categories))
581 (forward-char) 550 (forward-char 1)
582 (insert (format "%s%s%s\n%s\n%s %s\n" 551 (insert (format "%s%s%s\n%s\n%s %s\n"
583 todo-prefix todo-category-beg cat 552 todo-prefix todo-category-beg cat
584 todo-category-end 553 todo-category-end
585 todo-prefix todo-category-sep)) 554 todo-prefix todo-category-sep)))
586 (save-buffer))
587 0) 555 0)
588 556
557 ;;;### autoload
589 (defun todo-add-item-non-interactively (new-item category) 558 (defun todo-add-item-non-interactively (new-item category)
590 "Insert NEW-ITEM in TODO list as a new entry in CATEGORY." 559 "Insert NEW-ITEM in TODO list as a new entry in CATEGORY."
591 (save-excursion 560 (save-excursion
592 (todo-show)) 561 (todo-show))
593 (save-excursion 562 (save-excursion
594 (if (string= "" category) 563 (if (string= "" category)
595 (setq category (nth todo-category-number todo-categories))) 564 (setq category (nth todo-category-number todo-categories)))
596 (setq todo-category-number 565 (let ((cat-exists (member category todo-categories)))
597 (or (todo-position category todo-categories) 566 (setq todo-category-number
598 (todo-add-category category))) 567 (if cat-exists
568 (- (length todo-categories) (length cat-exists))
569 (todo-add-category category))))
599 (todo-show) 570 (todo-show)
600 (setq todo-previous-line 0) 571 (setq todo-previous-line 0)
601 (let ((top 1) 572 (let ((top 1)
602 (bottom (1+ (count-lines (point-min) (point-max))))) 573 (bottom (1+ (count-lines (point-min) (point-max)))))
603 (while (> (- bottom top) todo-insert-threshold) 574 (while (> (- bottom top) todo-insert-threshold)
604 (let* ((current (/ (+ top bottom) 2)) 575 (let* ((current (/ (+ top bottom) 2))
605 (answer (if (< current bottom) 576 (answer (if (< current bottom)
606 (todo-more-important-p current)))) 577 (todo-more-important-p current) nil)))
607 (if answer 578 (if answer
608 (setq bottom current) 579 (setq bottom current)
609 (setq top (1+ current))))) 580 (setq top (1+ current)))))
610 (setq top (/ (+ top bottom) 2)) 581 (setq top (/ (+ top bottom) 2))
611 ;; goto-line doesn't have the desired behavior in a narrowed buffer 582 ;; goto-line doesn't have the desired behavior in a narrowed buffer
612 (goto-char (point-min)) 583 (goto-char (point-min))
613 (forward-line (1- top))) 584 (forward-line (1- top)))
614 (insert new-item ?\n) 585 (insert new-item "\n")
615 (todo-backward-item) 586 (todo-backward-item)
616 (progn ;;; horrible os10000 hack to make items appear when inserting into empty buffer
617 (widen)
618 (show-all)
619 (todo-forward-category)
620 (todo-backward-category))
621 (todo-save) 587 (todo-save)
622 (message ""))) 588 (message "")))
623 589
624 ;;;###autoload 590 ;;;### autoload
625 (defun todo-insert-item (arg) 591 (defun todo-insert-item (arg)
626 "Insert new TODO list entry. 592 "Insert new TODO list entry.
627 With a prefix argument solicit the category, otherwise use the current 593 With a prefix argument solicit the category, otherwise use the current
628 category." 594 category."
629 (interactive "P") 595 (interactive "P")
638 (history (cons 'categories (1+ todo-category-number))) 604 (history (cons 'categories (1+ todo-category-number)))
639 (current-category (nth todo-category-number todo-categories)) 605 (current-category (nth todo-category-number todo-categories))
640 (category 606 (category
641 (if arg 607 (if arg
642 current-category 608 current-category
643 (completing-read 609 (completing-read (concat "Category [" current-category "]: ")
644 (concat "Category [" current-category "]: ") 610 (todo-category-alist) nil nil nil
645 (todo-category-alist) nil nil nil history current-category)))) 611 history current-category))))
646 (todo-add-item-non-interactively new-item category)))) 612 (todo-add-item-non-interactively new-item category))))
647 613
648 (defalias 'todo-cmd-inst 'todo-insert-item) 614 (defalias 'todo-cmd-inst 'todo-insert-item)
649 615
616 ;;;### autoload
650 (defun todo-insert-item-here () 617 (defun todo-insert-item-here ()
651 "Insert new TODO list entry under the cursor." 618 "Insert new TODO list entry under the cursor."
652 (interactive "") 619 (interactive "")
653 (save-excursion 620 (save-excursion
654 (if (not (string-equal mode-name "TODO")) (todo-show)) 621 (if (not (string-equal mode-name "TODO")) (todo-show))
670 (setq todo-previous-answer 637 (setq todo-previous-answer
671 (y-or-n-p (concat "More important than '" item "'? ")))))) 638 (y-or-n-p (concat "More important than '" item "'? "))))))
672 todo-previous-answer) 639 todo-previous-answer)
673 (defalias 'todo-ask-p 'todo-more-important-p) 640 (defalias 'todo-ask-p 'todo-more-important-p)
674 641
675 (defun todo-delete-item () "Delete current TODO list entry." 642 (defun todo-delete-item ()
643 "Delete current TODO list entry."
676 (interactive) 644 (interactive)
677 (if (> (count-lines (point-min) (point-max)) 0) 645 (if (> (count-lines (point-min) (point-max)) 0)
678 (let* ((todo-entry (todo-item-string-start)) 646 (let* ((todo-entry (todo-item-string-start))
679 (todo-answer (y-or-n-p (concat "Permanently remove '" 647 (todo-answer (y-or-n-p (concat "Permanently remove '"
680 todo-entry "'? ")))) 648 todo-entry "'? "))))
681 (when todo-answer 649 (if todo-answer
682 (todo-remove-item) 650 (progn
683 (todo-backward-item)) 651 (todo-remove-item)
652 (todo-backward-item)))
684 (message "")) 653 (message ""))
685 (error "No TODO list entry to delete"))) 654 (error "No TODO list entry to delete")))
686 (defalias 'todo-cmd-kill 'todo-delete-item) 655 (defalias 'todo-cmd-kill 'todo-delete-item)
687 656
688 (defun todo-raise-item () "Raise priority of current entry." 657 (defun todo-raise-item ()
658 "Raise priority of current entry."
689 (interactive) 659 (interactive)
690 (if (> (count-lines (point-min) (point)) 0) 660 (if (> (count-lines (point-min) (point)) 0)
691 (let ((item (todo-item-string))) 661 (let ((item (todo-item-string)))
692 (todo-remove-item) 662 (todo-remove-item)
693 (todo-backward-item) 663 (todo-backward-item)
694 (save-excursion 664 (save-excursion
695 (insert item ?\n)) 665 (insert item "\n"))
696 (message "")) 666 (message ""))
697 (error "No TODO list entry to raise"))) 667 (error "No TODO list entry to raise")))
698 (defalias 'todo-cmd-raise 'todo-raise-item) 668 (defalias 'todo-cmd-rais 'todo-raise-item)
699 669
700 (defun todo-lower-item () "Lower priority of current entry." 670 (defun todo-lower-item ()
671 "Lower priority of current entry."
701 (interactive) 672 (interactive)
702 (if (> (count-lines (point) (point-max)) 1) 673 (if (> (count-lines (point) (point-max)) 1)
703 ;; Assume there is a final newline 674 ;; Assume there is a final newline
704 (let ((item (todo-item-string))) 675 (let ((item (todo-item-string)))
705 (todo-remove-item) 676 (todo-remove-item)
706 (todo-forward-item) 677 (todo-forward-item)
707 (save-excursion 678 (save-excursion
708 (insert item ?\n)) 679 (insert item "\n"))
709 (message "")) 680 (message ""))
710 (error "No TODO list entry to lower"))) 681 (error "No TODO list entry to lower")))
711 (defalias 'todo-cmd-lowr 'todo-lower-item) 682 (defalias 'todo-cmd-lowr 'todo-lower-item)
712 683
713 (defun todo-file-item (&optional comment) 684 (defun todo-file-item (&optional comment)
714 "File the current TODO list entry away, annotated with an optional COMMENT." 685 "File the current TODO list entry away, annotated with an optional COMMENT."
715 (interactive "sComment: ") 686 (interactive "sComment: ")
716 (or (> (count-lines (point-min) (point-max)) 0) 687 (or (> (count-lines (point-min) (point-max)) 0)
717 (error "No TODO list entry to file away")) 688 (error "No TODO list entry to file away"))
718 (let ((time-stamp-format todo-time-string-format)) 689 (let ((time-stamp-format todo-time-string-format))
719 (when (and comment (> (length comment) 0)) 690 (if (and comment (> (length comment) 0))
720 (goto-char (todo-item-end)) 691 (progn
721 (insert 692 (goto-char (todo-item-end))
722 (if (save-excursion (beginning-of-line) 693 (insert
723 (looking-at (regexp-quote todo-prefix))) 694 (if (save-excursion (beginning-of-line)
724 " " 695 (looking-at (regexp-quote todo-prefix)))
725 "\n\t") 696 " "
726 "(" comment ")")) 697 "\n\t")
698 "(" comment ")")))
727 (goto-char (todo-item-end)) 699 (goto-char (todo-item-end))
728 (insert " [" (nth todo-category-number todo-categories) "]") 700 (insert " [" (nth todo-category-number todo-categories) "]")
729 (goto-char (todo-item-start)) 701 (goto-char (todo-item-start))
730 (let ((temp-point (point))) 702 (let ((temp-point (point)))
731 (if (looking-at (regexp-quote todo-prefix)) 703 (if (looking-at (regexp-quote todo-prefix))
740 712
741 ;; --------------------------------------------------------------------------- 713 ;; ---------------------------------------------------------------------------
742 714
743 ;; Utility functions: 715 ;; Utility functions:
744 716
717
718 ;;;###autoload
745 (defun todo-top-priorities (&optional nof-priorities category-pr-page) 719 (defun todo-top-priorities (&optional nof-priorities category-pr-page)
746 "List top priorities for each category. 720 "List top priorities for each category.
747 721
748 Number of entries for each category is given by NOF-PRIORITIES which 722 Number of entries for each category is given by NOF-PRIORITIES which
749 defaults to \'todo-show-priorities\'. 723 defaults to \'todo-show-priorities\'.
753 727
754 (interactive "P") 728 (interactive "P")
755 (or nof-priorities (setq nof-priorities todo-show-priorities)) 729 (or nof-priorities (setq nof-priorities todo-show-priorities))
756 (if (listp nof-priorities) ;universal argument 730 (if (listp nof-priorities) ;universal argument
757 (setq nof-priorities (car nof-priorities))) 731 (setq nof-priorities (car nof-priorities)))
758 (let ((todo-print-buffer-name " *todo-tmp*") 732 (let ((todo-print-buffer-name "*Tmp*")
759 ;;(todo-print-category-number 0) 733 ;;(todo-print-category-number 0)
760 (todo-category-break (if category-pr-page " " "")) 734 (todo-category-break (if category-pr-page " " ""))
761 (cat-end 735 (cat-end
762 (concat 736 (concat
763 (if todo-remove-separator 737 (if todo-remove-separator
770 (save-restriction 744 (save-restriction
771 (widen) 745 (widen)
772 (copy-to-buffer todo-print-buffer-name (point-min) (point-max)) 746 (copy-to-buffer todo-print-buffer-name (point-min) (point-max))
773 (set-buffer todo-print-buffer-name) 747 (set-buffer todo-print-buffer-name)
774 (goto-char (point-min)) 748 (goto-char (point-min))
775 (when (re-search-forward (regexp-quote todo-header) nil t) 749 (if (re-search-forward (regexp-quote todo-header) nil t)
776 (beginning-of-line 1) 750 (progn
777 (kill-line)) ;Remove mode line 751 (beginning-of-line 1)
752 (kill-line))) ;Remove mode line
778 (while (re-search-forward ;Find category start 753 (while (re-search-forward ;Find category start
779 (regexp-quote (concat todo-prefix todo-category-beg)) 754 (regexp-quote (concat todo-prefix todo-category-beg))
780 nil t) 755 nil t)
781 (setq beg (+ (line-end-position) 1)) ;Start of first entry. 756 (setq beg (+ (line-end-position) 1)) ;Start of first entry.
782 (re-search-forward cat-end nil t) 757 (re-search-forward cat-end nil t)
783 (setq end (match-beginning 0)) 758 (setq end (match-beginning 0))
784 (replace-match todo-category-break) 759 (replace-match todo-category-break)
785 (narrow-to-region beg end) ;In case we have too few entries. 760 (narrow-to-region beg end) ;In case we have too few entries.
786 (goto-char (point-min)) 761 (goto-char (point-min))
787 (if (= 0 nof-priorities) ;Traverse entries. 762 (if (= 0 nof-priorities) ;Traverse entries.
788 (goto-char end) ;All entries 763 (goto-char end) ;All entries
789 (todo-forward-item nof-priorities)) 764 (todo-forward-item nof-priorities))
790 (setq beg (point)) 765 (setq beg (point))
791 (delete-region beg end) 766 (delete-region beg end)
792 (widen)) 767 (widen))
793 (and (looking-at " ") (replace-match "")) ;Remove trailing form-feed. 768 (and (looking-at " ") (replace-match "")) ;Remove trailing form-feed.
798 ;; Else we could have used pop-to-buffer. 773 ;; Else we could have used pop-to-buffer.
799 (display-buffer todo-print-buffer-name) 774 (display-buffer todo-print-buffer-name)
800 (message "Type C-x 1 to remove %s window. M-C-v to scroll the help." 775 (message "Type C-x 1 to remove %s window. M-C-v to scroll the help."
801 todo-print-buffer-name))) 776 todo-print-buffer-name)))
802 777
778 ;;;###autoload
803 (defun todo-save-top-priorities (&optional nof-priorities) 779 (defun todo-save-top-priorities (&optional nof-priorities)
804 "Save top priorities for each category in `todo-file-top'. 780 "Save top priorities for each category in `todo-file-top'.
805 781
806 Number of entries for each category is given by NOF-PRIORITIES which 782 Number of entries for each category is given by NOF-PRIORITIES which
807 defaults to `todo-show-priorities'." 783 defaults to `todo-show-priorities'."
808 (interactive "P") 784 (interactive "P")
809 (with-temp-buffer 785 (save-window-excursion
810 (todo-top-priorities nof-priorities) 786 (save-excursion
811 (write-file todo-file-top))) 787 (save-restriction
788 (todo-top-priorities nof-priorities)
789 (set-buffer todo-tmp-buffer-name)
790 (write-file todo-file-top)
791 (kill-this-buffer)))))
812 792
813 ;;;###autoload 793 ;;;###autoload
814 (defun todo-print (&optional category-pr-page) 794 (defun todo-print (&optional category-pr-page)
815 "Print todo summary using `todo-print-function'. 795 "Print todo summary using `todo-print-function'.
816 If CATEGORY-PR-PAGE is non-nil, a page separator \'^L\' is inserted 796 If CATEGORY-PR-PAGE is non-nil, a page separator `^L' is inserted
817 between each category. 797 between each category.
818 798
819 Number of entries for each category is given by 799 Number of entries for each category is given by `todo-print-priorities'."
820 \'todo-print-priorities\'."
821 (interactive "P") 800 (interactive "P")
822 (with-temp-buffer 801 (save-window-excursion
823 (todo-top-priorities todo-print-priorities 802 (save-excursion
803 (save-restriction
804 (todo-top-priorities todo-print-priorities
824 category-pr-page) 805 category-pr-page)
825 (funcall todo-print-function) 806 (set-buffer todo-tmp-buffer-name)
826 (message "Todo printing done."))) 807 (and (funcall todo-print-function)
808 (kill-this-buffer))
809 (message "Todo printing done.")))))
827 810
828 (defun todo-jump-to-category () 811 (defun todo-jump-to-category ()
829 "Jump to a category. Default is previous category." 812 "Jump to a category. Default is previous category."
830 (interactive) 813 (interactive)
831 (let* ((categories todo-categories) 814 (let* ((categories todo-categories)
832 (history (cons 'categories (1+ todo-category-number))) 815 (history (cons 'categories (1+ todo-category-number)))
816 (default (nth todo-category-number todo-categories))
833 (category (completing-read 817 (category (completing-read
834 (concat "Category [" 818 (concat "Category [" default "]: ")
835 (nth todo-category-number todo-categories) "]: ") 819 (todo-category-alist) nil nil nil history default)))
836 (todo-category-alist) nil nil nil history)))
837 (if (string= "" category) 820 (if (string= "" category)
838 (setq category (nth todo-category-number todo-categories))) 821 (setq category (nth todo-category-number todo-categories)))
839 (setq todo-category-number 822 (setq todo-category-number
840 (or (todo-position category todo-categories) 823 (if (member category todo-categories)
841 (todo-add-category category))) 824 (- (length todo-categories)
825 (length (member category todo-categories)))
826 (todo-add-category category)))
842 (todo-show))) 827 (todo-show)))
843 828
844 (defun todo-line-string () 829 (defun todo-line-string ()
845 "Return current line in buffer as a string." 830 "Return current line in buffer as a string."
846 (buffer-substring (line-beginning-position) (line-end-position))) 831 (buffer-substring (line-beginning-position) (line-end-position)))
872 857
873 (defun todo-remove-item () 858 (defun todo-remove-item ()
874 "Delete the current entry from the TODO list." 859 "Delete the current entry from the TODO list."
875 (delete-region (todo-item-start) (1+ (todo-item-end)))) 860 (delete-region (todo-item-start) (1+ (todo-item-end))))
876 861
877 (defun todo-item-string () "Return current TODO list entry as a string." 862 (defun todo-item-string ()
863 "Return current TODO list entry as a string."
878 (buffer-substring (todo-item-start) (todo-item-end))) 864 (buffer-substring (todo-item-start) (todo-item-end)))
879 865
880 (defun todo-string-count-lines (string) 866 (defun todo-string-count-lines (string)
881 "Return the number of lines STRING spans." 867 "Return the number of lines STRING spans."
882 (length (split-string string "\n"))) 868 (length (split-string string "\n")))
889 "Generate an alist for use in `completing-read' from `todo-categories'." 875 "Generate an alist for use in `completing-read' from `todo-categories'."
890 (mapcar #'list todo-categories)) 876 (mapcar #'list todo-categories))
891 877
892 ;; --------------------------------------------------------------------------- 878 ;; ---------------------------------------------------------------------------
893 879
894 (easy-menu-define 880 (easy-menu-define todo-menu todo-mode-map "Todo Menu"
895 todo-menu todo-mode-map "Todo Menu" 881 '("Todo"
896 '("Todo" 882 ["Next category" todo-forward-category t]
897 ["Next category" todo-forward-category t] 883 ["Previous category" todo-backward-category t]
898 ["Previous category" todo-backward-category t] 884 ["Jump to category" todo-jump-to-category t]
899 ["Jump to category" todo-jump-to-category t] 885 ["Show top priority items" todo-top-priorities t]
900 ["Show top priority items" todo-top-priorities t] 886 ["Print categories" todo-print t]
901 ["Print categories" todo-print t] 887 "---"
902 "---" 888 ["Edit item" todo-edit-item t]
903 ["Edit item" todo-edit-item t] 889 ["File item" todo-file-item t]
904 ["File item" todo-file-item t] 890 ["Insert new item" todo-insert-item t]
905 ["Insert new item" todo-insert-item t] 891 ["Insert item here" todo-insert-item-here t]
906 ["Insert item here" todo-insert-item-here t] 892 ["Kill item" todo-delete-item t]
907 ["Kill item" todo-delete-item t] 893 "---"
908 "---" 894 ["Lower item priority" todo-lower-item t]
909 ["Lower item priority" todo-lower-item t] 895 ["Raise item priority" todo-raise-item t]
910 ["Raise item priority" todo-raise-item t] 896 "---"
911 "---" 897 ["Next item" todo-forward-item t]
912 ["Next item" todo-forward-item t] 898 ["Previous item" todo-backward-item t]
913 ["Previous item" todo-backward-item t] 899 "---"
914 "---" 900 ["Save" todo-save t]
915 ["Save" todo-save t] 901 ["Save Top Priorities" todo-save-top-priorities t]
916 ["Save Top Priorities" todo-save-top-priorities t] 902 "---"
917 "---" 903 ["Quit" todo-quit t]
918 ["Quit" todo-quit t] 904 ))
919 ))
920 905
921 ;; As calendar reads .todo-do before todo-mode is loaded. 906 ;; As calendar reads .todo-do before todo-mode is loaded.
922 ;;;###autoload 907 ;;;### autoload
923 (defun todo-mode () 908 (defun todo-mode ()
924 "Major mode for editing TODO lists. 909 "Major mode for editing TODO lists.
925 910
926 \\{todo-mode-map}" 911 \\{todo-mode-map}"
927 (interactive) 912 (interactive)
928 (setq major-mode 'todo-mode) 913 (setq major-mode 'todo-mode)
929 (setq mode-name "TODO") 914 (setq mode-name "TODO")
930 (use-local-map todo-mode-map) 915 (use-local-map todo-mode-map)
931 (easy-menu-add todo-menu) 916 (easy-menu-add todo-menu)
932 (setq fill-prefix "\t\t")
933 (let ((prefix (regexp-quote todo-prefix)))
934 (setq paragraph-separate prefix)
935 (setq outline-regexp prefix))
936 (outline-minor-mode 1)
937 (goto-char (point-min))
938 (outline-next-heading) ; get past -*- line
939 (hide-other)
940 (auto-fill-mode 1)
941 (run-hooks 'todo-mode-hook)) 917 (run-hooks 'todo-mode-hook))
942 918
919 (eval-when-compile
920 (defvar date)
921 (defvar entry))
922
943 ;; Read about this function in the setup instructions above! 923 ;; Read about this function in the setup instructions above!
944 ;;;###autoload 924 ;;;### autoload
945 (defun todo-cp () 925 (defun todo-cp ()
946 "Make a diary entry appear only in the current date's diary." 926 "Make a diary entry appear only in the current date's diary."
947 (if (equal (calendar-current-date) date) 927 (if (equal (calendar-current-date) date)
948 entry)) 928 entry))
949 929
950 (defun todo-edit-mode () 930 (define-derived-mode todo-edit-mode text-mode "TODO Edit"
951 "Major mode for editing items in the TODO list. 931 "Major mode for editing items in the TODO list.
952 932
953 \\{todo-edit-mode-map}" 933 \\{todo-edit-mode-map}")
954 (text-mode) 934
955 (setq major-mode 'todo-edit-mode) 935 ;;;### autoload
956 (setq mode-name "TODO Edit")
957 (run-hooks 'todo-edit-mode-hook))
958
959 ;;;###autoload
960 (defun todo-show () 936 (defun todo-show ()
961 "Show TODO list." 937 "Show TODO list."
962 (interactive) 938 (interactive)
963 (if (file-exists-p todo-file-do) 939 (if (file-exists-p todo-file-do)
964 (find-file todo-file-do) 940 (find-file todo-file-do)
965 (todo-initial-setup)) 941 (todo-initial-setup))
966 (unless todo-categories 942 (if (null todo-categories)
967 (if (null todo-cats) 943 (if (null todo-cats)
968 (error "Error in %s: No categories in list `todo-categories'" 944 (error "Error in %s: No categories in list `todo-categories'"
969 todo-file-do) 945 todo-file-do)
970 (goto-char (point-min)) 946 (goto-char (point-min))
971 (and (search-forward "todo-cats:" nil t) 947 (and (search-forward "todo-cats:" nil t)
972 (replace-match "todo-categories:")) 948 (replace-match "todo-categories:"))
973 (make-local-variable 'todo-categories) 949 (make-local-variable 'todo-categories)
974 (setq todo-categories todo-cats))) 950 (setq todo-categories todo-cats)))
975 (beginning-of-line) 951 (beginning-of-line)
976 (todo-category-select)) 952 (todo-category-select))
977 953
978 (defun todo-initial-setup () 954 (defun todo-initial-setup ()
979 "Set up things to work properly in TODO mode." 955 "Set up things to work properly in TODO mode."
982 (todo-mode) 958 (todo-mode)
983 (todo-add-category "Todo")) 959 (todo-add-category "Todo"))
984 960
985 (provide 'todo-mode) 961 (provide 'todo-mode)
986 962
987 ;; ---------------------------------------------------------------------------
988 ;;; todo-mode.el ends here 963 ;;; todo-mode.el ends here
989 ;; ---------------------------------------------------------------------------