aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/calendar/todos.el196
1 files changed, 179 insertions, 17 deletions
diff --git a/lisp/calendar/todos.el b/lisp/calendar/todos.el
index 1689c80d820..591cf690758 100644
--- a/lisp/calendar/todos.el
+++ b/lisp/calendar/todos.el
@@ -282,11 +282,11 @@ show and mark todo entries for today, but may slow down processing of
282the diary file somewhat." 282the diary file somewhat."
283 :type 'string 283 :type 'string
284 :group 'todos) 284 :group 'todos)
285(defcustom todos-file-do (convert-standard-filename "~/.todos-do") 285(defcustom todos-file-do (convert-standard-filename "~/.emacs.d/.todos-do")
286 "TODO mode list file." 286 "TODO mode list file."
287 :type 'file 287 :type 'file
288 :group 'todos) 288 :group 'todos)
289(defcustom todos-file-done (convert-standard-filename "~/.todos-done") 289(defcustom todos-file-done (convert-standard-filename "~/.emacs.d/.todos-done")
290 "TODO mode archive file." 290 "TODO mode archive file."
291 :type 'file 291 :type 'file
292 :group 'todos) 292 :group 'todos)
@@ -347,6 +347,10 @@ Automatically generated when `todos-save-top-priorities' is non-nil."
347 "Non-nil makes `todos-save' automatically save top-priorities in `todos-file-top'." 347 "Non-nil makes `todos-save' automatically save top-priorities in `todos-file-top'."
348 :type 'boolean 348 :type 'boolean
349 :group 'todos) 349 :group 'todos)
350(defcustom todos-completion-ignore-case t ;; FIXME: nil for release
351 "Non-nil means don't consider case significant in todos-completing-read."
352 :type 'boolean
353 :group 'todos)
350 354
351;; Thanks for the ISO time stamp format go to Karl Eichwalder <ke@suse.de> 355;; Thanks for the ISO time stamp format go to Karl Eichwalder <ke@suse.de>
352;; My format string for the appt.el package is "%3b %2d, %y, %02I:%02M%p". 356;; My format string for the appt.el package is "%3b %2d, %y, %02I:%02M%p".
@@ -372,6 +376,29 @@ For details see the variable `time-stamp-format'."
372 (let ((time-stamp-format todos-time-string-format)) 376 (let ((time-stamp-format todos-time-string-format))
373 (concat (time-stamp-string) " " todos-initials ": "))) 377 (concat (time-stamp-string) " " todos-initials ": ")))
374 378
379(defface todos-prefix-string
380 '((t
381 :inherit font-lock-variable-name-face
382 ))
383 "Face for Todos prefix string."
384 :group 'todos)
385(defvar todos-prefix-face 'todos-prefix-string)
386
387(defface todos-item-header
388 '((t
389 :inherit font-lock-function-name-face
390 ))
391 "Face for Todos item header string."
392 :group 'todos)
393(defvar todos-item-header-face 'todos-item-header)
394
395(defvar todos-font-lock-keywords
396 (list
397 (list (concat "^" (regexp-quote todos-prefix)) 0 'todos-prefix-face t)
398 (list (concat "^" (regexp-quote todos-prefix) "\\(.*[0-9]+ [A-Ba-z0-9]*\\]?:\\)")
399 1 'todos-item-header-face t))
400 "Font-locking for Todos mode.")
401
375;; --------------------------------------------------------------------------- 402;; ---------------------------------------------------------------------------
376 403
377;; Set up some helpful context ... 404;; Set up some helpful context ...
@@ -390,7 +417,10 @@ For details see the variable `time-stamp-format'."
390 (suppress-keymap map t) 417 (suppress-keymap map t)
391 (define-key map "+" 'todos-forward-category) 418 (define-key map "+" 'todos-forward-category)
392 (define-key map "-" 'todos-backward-category) 419 (define-key map "-" 'todos-backward-category)
420 (define-key map "A" 'todos-add-category)
421 (define-key map "C" 'todos-display-categories)
393 (define-key map "d" 'todos-file-item) ;done/delete 422 (define-key map "d" 'todos-file-item) ;done/delete
423 (define-key map "D" 'todos-delete-category)
394 (define-key map "e" 'todos-edit-item) 424 (define-key map "e" 'todos-edit-item)
395 (define-key map "E" 'todos-edit-multiline) 425 (define-key map "E" 'todos-edit-multiline)
396 (define-key map "f" 'todos-file-item) 426 (define-key map "f" 'todos-file-item)
@@ -399,11 +429,13 @@ For details see the variable `time-stamp-format'."
399 (define-key map "j" 'todos-jump-to-category) 429 (define-key map "j" 'todos-jump-to-category)
400 (define-key map "k" 'todos-delete-item) 430 (define-key map "k" 'todos-delete-item)
401 (define-key map "l" 'todos-lower-item) 431 (define-key map "l" 'todos-lower-item)
432 (define-key map "m" 'todos-move-item)
402 (define-key map "n" 'todos-forward-item) 433 (define-key map "n" 'todos-forward-item)
403 (define-key map "p" 'todos-backward-item) 434 (define-key map "p" 'todos-backward-item)
404 (define-key map "P" 'todos-print) 435 (define-key map "P" 'todos-print)
405 (define-key map "q" 'todos-quit) 436 (define-key map "q" 'todos-quit)
406 (define-key map "r" 'todos-raise-item) 437 (define-key map "r" 'todos-raise-item)
438 (define-key map "R" 'todos-rename-category)
407 (define-key map "s" 'todos-save) 439 (define-key map "s" 'todos-save)
408 (define-key map "S" 'todos-save-top-priorities) 440 (define-key map "S" 'todos-save-top-priorities)
409 (define-key map "t" 'todos-top-priorities) 441 (define-key map "t" 'todos-top-priorities)
@@ -423,6 +455,13 @@ For details see the variable `time-stamp-format'."
423(defvar todos-category-end "--- End" 455(defvar todos-category-end "--- End"
424 "Separator after a category.") 456 "Separator after a category.")
425 457
458(defvar todos-window-configuration nil
459 "Variable for storing current window configuration in Todos mode.
460
461Set before leaving Todos mode buffer by todos-display-categories.
462Restored before re-entering Todo mode buffer by todo-kill-buffer
463and todo-jump-to-category-noninteractively.")
464
426;; --------------------------------------------------------------------------- 465;; ---------------------------------------------------------------------------
427 466
428(defun todos-category-select () 467(defun todos-category-select ()
@@ -584,6 +623,102 @@ For details see the variable `time-stamp-format'."
584 (todos-save) 623 (todos-save)
585 (message ""))) 624 (message "")))
586 625
626(defun todos-rename-category (new)
627 "Rename current Todos category."
628 (interactive "sCategory: ")
629 (let ((cat (nth todos-category-number todos-categories))
630 (vec (vconcat todos-categories))
631 prompt)
632 (while (and (cond ((string= "" new)
633 (setq prompt "Enter a non-empty category name: "))
634 ((string-match "\\`\\s-+\\'" new)
635 (setq prompt "Enter a category name that is not only white space: "))
636 ((member new todos-categories)
637 (setq prompt "Enter a non-existing category name: ")))
638 (setq new (read-from-minibuffer prompt))))
639 (aset vec todos-category-number new)
640 (setq todos-categories (append vec nil))
641 (save-excursion
642 (widen)
643 (search-backward (concat todos-prefix todos-category-beg))
644 (goto-char (match-end 0))
645 (when (looking-at (regexp-quote cat))
646 (replace-match new t))
647 (goto-char (point-min))
648 (setq mode-line-buffer-identification
649 (concat "Category: " new))))
650;; (concat "Category: " (format "%18s" new)))))
651 (todos-category-select))
652
653(defun todos-delete-category ()
654 "Delete current Todos category provided it is empty."
655 (interactive)
656 (if (not (eq (point-max) (point-min)))
657 (message "This category is not empty, so it cannot be deleted")
658 (let ((cat (nth todos-category-number todos-categories)) beg end)
659 (when (y-or-n-p (concat "Permanently remove category '" cat "'? "))
660 (widen)
661 (setq beg (re-search-backward
662 (concat "^" (regexp-quote todos-prefix) todos-category-beg cat)
663 (point-min) nil)
664 end (1+ (re-search-forward
665 (concat "^" todos-category-end "\n"
666 (regexp-quote todos-prefix) " " todos-category-sep)
667 (point-max) nil)))
668 (kill-region beg end)
669 (setq todos-categories (delete cat todos-categories))
670 (todos-category-select)
671 (message "Deleted category \"%s\"" cat)))))
672
673(defcustom todos-categories-buffer "*TODOS Categories*"
674 "Name of buffer displayed by `todos-display-categories'"
675 :type 'string
676 :group 'todos)
677
678(defun todos-display-categories ()
679 "Display an alphabetical list of clickable Todos category names.
680Click or type RET on a category name to go to it."
681 (interactive)
682 (setq todos-window-configuration (current-window-configuration))
683 (let ((categories (copy-sequence todos-categories))
684 beg)
685 ;; alphabetize the list case insensitively
686 (setq categories (sort categories (lambda (s1 s2) (let ((cis1 (upcase s1))
687 (cis2 (upcase s2)))
688 (string< cis1 cis2)))))
689 (require 'widget)
690 (eval-when-compile
691 (require 'wid-edit))
692 (with-current-buffer (get-buffer-create todos-categories-buffer)
693 (pop-to-buffer (current-buffer))
694 (erase-buffer)
695 (kill-all-local-variables)
696 (widget-insert "Press a button to display the corresponding category.\n\n")
697 (setq beg (point))
698 (mapc (lambda (cat)
699 (widget-create 'push-button
700 :notify (lambda (widget &rest ignore)
701 (todos-jump-to-category-noninteractively
702 (widget-get widget :value)))
703
704 cat)
705 (widget-insert "\n"))
706 categories)
707 (use-local-map widget-keymap)
708 (widget-setup))))
709
710(defun todos-jump-to-category-noninteractively (cat)
711 (let ((name todos-categories-buffer))
712 (if (string= (buffer-name) name)
713 (kill-buffer name)))
714 (set-window-configuration todos-window-configuration)
715 (switch-to-buffer (file-name-nondirectory todos-file-do))
716 (widen)
717 (goto-char (point-min))
718 (setq todos-category-number (- (length todos-categories)
719 (length (member cat todos-categories))))
720 (todos-category-select))
721
587;;;###autoload 722;;;###autoload
588(defun todos-insert-item (arg) 723(defun todos-insert-item (arg)
589 "Insert new TODO list entry. 724 "Insert new TODO list entry.
@@ -666,6 +801,18 @@ If point is on an empty line, insert the entry there."
666 (message "")) 801 (message ""))
667 (error "No TODO list entry to lower"))) 802 (error "No TODO list entry to lower")))
668 803
804(defun todos-move-item ()
805 "Move the current todo item to another, interactively named, category.
806
807If the named category is not one of the current todo categories, then
808it is created and the item becomes the first entry in that category."
809 (interactive)
810 (let ((item (todos-item-string))
811 (inhibit-quit t)
812 (category (todos-completing-read)))
813 (todos-remove-item)
814 (todos-add-item-non-interactively item category)))
815
669(defun todos-file-item (&optional comment) 816(defun todos-file-item (&optional comment)
670 "File the current TODO list entry away, annotated with an optional COMMENT." 817 "File the current TODO list entry away, annotated with an optional COMMENT."
671 (interactive "sComment: ") 818 (interactive "sComment: ")
@@ -730,9 +877,9 @@ between each category."
730 (copy-to-buffer todos-print-buffer-name (point-min) (point-max)) 877 (copy-to-buffer todos-print-buffer-name (point-min) (point-max))
731 (set-buffer todos-print-buffer-name) 878 (set-buffer todos-print-buffer-name)
732 (goto-char (point-min)) 879 (goto-char (point-min))
733 (when (re-search-forward (regexp-quote todos-header) nil t) 880 ;; (when (re-search-forward (regexp-quote todos-header) nil t)
734 (beginning-of-line 1) 881 ;; (beginning-of-line 1)
735 (delete-region (point) (line-end-position))) 882 ;; (delete-region (point) (line-end-position)))
736 (while (re-search-forward ;Find category start 883 (while (re-search-forward ;Find category start
737 (regexp-quote (concat todos-prefix todos-category-beg)) 884 (regexp-quote (concat todos-prefix todos-category-beg))
738 nil t) 885 nil t)
@@ -866,17 +1013,22 @@ Number of entries for each category is given by `todos-print-priorities'."
866 1013
867(defun todos-completing-read () 1014(defun todos-completing-read ()
868 "Return a category name, with completion, for use in Todo mode." 1015 "Return a category name, with completion, for use in Todo mode."
869 ;; make a copy of todos-categories in case history-delete-duplicates is 1016 ;; allow SPC to insert spaces, for adding new category names with
870 ;; non-nil, which makes completing-read alter todos-categories 1017 ;; todos-move-item
871 (let* ((categories (copy-sequence todos-categories)) 1018 (let ((map minibuffer-local-completion-map))
872 (history (cons 'todos-categories (1+ todos-category-number))) 1019 (define-key map " " nil)
873 (default (nth todos-category-number todos-categories)) 1020 ;; make a copy of todos-categories in case history-delete-duplicates is
874 (category (completing-read 1021 ;; non-nil, which makes completing-read alter todos-categories
875 (concat "Category [" default "]: ") 1022 (let* ((categories (copy-sequence todos-categories))
876 todos-categories nil nil nil history default))) 1023 (history (cons 'todos-categories (1+ todos-category-number)))
877 ;; restore the original value of todos-categories 1024 (default (nth todos-category-number todos-categories))
878 (setq todos-categories categories) 1025 (completion-ignore-case todos-completion-ignore-case)
879 category)) 1026 (category (completing-read
1027 (concat "Category [" default "]: ")
1028 todos-categories nil nil nil history default)))
1029 ;; restore the original value of todos-categories
1030 (setq todos-categories categories)
1031 category)))
880 1032
881;; --------------------------------------------------------------------------- 1033;; ---------------------------------------------------------------------------
882 1034
@@ -915,9 +1067,19 @@ Number of entries for each category is given by `todos-print-priorities'."
915 (interactive) 1067 (interactive)
916 (kill-all-local-variables) 1068 (kill-all-local-variables)
917 (setq major-mode 'todos-mode) 1069 (setq major-mode 'todos-mode)
918 (setq mode-name "TODO") 1070 (setq mode-name "TODOS")
919 (use-local-map todos-mode-map) 1071 (use-local-map todos-mode-map)
920 (easy-menu-add todos-menu) 1072 (easy-menu-add todos-menu)
1073 (make-local-variable 'font-lock-defaults)
1074 (setq font-lock-defaults '(todos-font-lock-keywords t))
1075 (make-local-variable 'word-wrap)
1076 (setq word-wrap t)
1077 (make-local-variable 'wrap-prefix)
1078 (setq wrap-prefix
1079 (make-string (1+ (length (concat todos-prefix
1080 (todos-entry-timestamp-initials)))) 32))
1081 (unless (member '(continuation) fringe-indicator-alist)
1082 (push '(continuation) fringe-indicator-alist))
921 (run-mode-hooks 'todos-mode-hook)) 1083 (run-mode-hooks 'todos-mode-hook))
922 1084
923(defvar date) 1085(defvar date)