* [PATCH] todo-mode extensions and bug fixes
@ 2002-05-28 23:03 Stephen Berman
2002-06-03 10:06 ` quick fix to todo-mode patch + followup question Stephen Berman
0 siblings, 1 reply; 2+ messages in thread
From: Stephen Berman @ 2002-05-28 23:03 UTC (permalink / raw)
[-- Attachment #1: Type: text/plain, Size: 1196 bytes --]
I have implemented a fair amount of additional functionality for
todo-mode and in the process fixed several bugs persisting in version
1.50, which is the current CVS version as well as the one included in
the released Emacs 21.2. A diff against this version and a ChangeLog
are attached below. This patch also corrects and hence supercedes
previous todo-mode patches I posted here on 26 November 2001 and 24
January 2002 (neither has been applied to the CVS source as of today).
I've tested the changes with Emacs 21.2 and 20.7, under both X and
tty.
Since the diff is rather long, it may be helpful to point out the most
significant changes (IMO): I've added functions to rename and to
delete TODO categories, to display a clickable list of categories, to
move TODO items from one category to another, to file items within
their category and display these under their category buffer, to add a
customizable string to separate items within a category, to change the
TODO item prefix string and to toggle its display within the category
buffer.
I welcome and would be grateful for any feedback about the new
functionality and any suggestions for improving the implementation.
--Steve Berman
[-- Attachment #2: todo-mode ChangeLog --]
[-- Type: application/octet-stream, Size: 3714 bytes --]
2002-05-28 Stephen Berman <steve@ims.uni-stuttgart.de>
* todo-mode-srb.el:
Additions and some changes to the initial commentary.
New defcustoms: todo-categories-mode-hook, todo-filed-mode-hook,
todo-item-separator.
New defvars: todo-filed-categories, todo-filed-mode-map,
todo-categories-mode-map, todo-categories-buffer,
todo-filed-buffer, todo-category-filed, todo-buffer-read-only,
todo-disabled-functions.
New defuns: todo-rename-category, todo-delete-category,
todo-display-categories, todo-file-item-under-cat,
todo-display-filed-items-under-cat, todo-move-item,
todo-change-item-sep-in-cat, todo-change-todo-prefix,
todo-toggle-item-header, todo-jump-to-category-noninteractively,
todo-category-click, todo-category-return, todo-item-separator-p,
todo-item-string-p, todo-first-in-category-p,
todo-last-in-category-p, todo-count-items, todo-advice,
todo-categories-mode, todo-filed-mode, todo-kill-buffer.
New menu: todo-filed-menu.
Require cl.el for pre-21.1 Emacsen.
Load user's custom-file if it exists.
(todo-mode-map): Add new and rebind some old key bindings.
(todo-remove-separator): Improve docstring.
(todo-category-select): Make it display only unfiled items,
accommodate todo-item-separator.
(todo-backward-item): Take account of todo-item-separator and fix
backwards movement from within an item.
(todo-forward-item): Don't go forward to empty last line.
(todo-edit-item): Accommodate buffer-read-only status, accommodate
todo-item-separator, make cursor position after editing the same
as before editing, update docstring to include filed items.
(todo-edit-multiline): Accommodate filed multiline items, update
docstring to reflect this.
(todo-add-category): Accommodate buffer-read-only status, add some
error checking, and make it properly display newly added category.
(todo-add-item-non-interactively): Accommodate buffer-read-only
status, accommodate new functionality (todo-item-separator,
todo-move-item), fix bug in calculation of new item's priority
involving multiline items, make cursor move to new item rather
than point-min.
(todo-insert-item): Remove save-excursion in order to put cursor
on new item, make the prefix argument do what the docstring says.
(todo-insert-item-here): Accommodate buffer-read-only status,
accommodate filed items, accommodate todo-item-separator, make
item insertion behave more naturally.
(todo-more-important-p): Substitute todo-forward-item for
forward-line.
(todo-delete-item): Accommodate buffer-read-only status,
accommodate todo-item-separator, correct cursor placement.
(todo-raise-item): Accommodate buffer-read-only status,
accommodate todo-item-separator.
(todo-lower-item): Accommodate buffer-read-only status,
accommodate todo-item-separator, fix behavior with empty category.
(todo-file-item): Accommodate buffer-read-only status, change
filed item comment format, remove comment solicitation from
interactive call, add functionality to file items that are filed
under their category in todo-file-done, update docstring.
(todo-item-end, todo-remove-item): Accommodate
todo-item-separator.
(todo-menu): New entries and corrections to existing entries.
(todo-mode): Call kill-all-local-variables, make and set local
variables next-line-add-newlines and buffer-read-only, call
todo-advice.
(todo-edit-mode): Substitute key-binding `q' to todo-kill-buffer
for key-binding `C-c C-c' to todo-edit-done.
(todo-show): Remove call to toggle-read-only.
(todo-initial-setup): Eliminate default "Todo" category to allow
user specified initial category, call write-file on todo-file-do
to prevent three instances of the initial category in the
todo-categories list.
[-- Attachment #3: todo-mode diff --]
[-- Type: application/octet-stream, Size: 67294 bytes --]
*** todo-mode.el~ Tue Jan 15 15:58:49 2002
--- todo-mode.el Tue May 28 22:57:52 2002
***************
*** 5,11 ****
;; Author: Oliver Seidel <os10000@seidel-space.de>
;; [Not clear the above works, July 2000]
;; Created: 2 Aug 1997
! ;; Version: $Id: todo-mode.el,v 1.50 2001/12/11 07:36:30 pj Exp $
;; Keywords: calendar, todo
;; This file is part of GNU Emacs.
--- 5,11 ----
;; Author: Oliver Seidel <os10000@seidel-space.de>
;; [Not clear the above works, July 2000]
;; Created: 2 Aug 1997
! ;; Version: $Id: todo-mode.el,v 1.51 2002/05/28 19:16:00 steve Exp $
;; Keywords: calendar, todo
;; This file is part of GNU Emacs.
***************
*** 97,103 ****
;;
;; Which version of todo-mode.el does this documentation refer to?
;;
! ;; $Id: todo-mode.el,v 1.50 2001/12/11 07:36:30 pj Exp $
;;
;; Pre-Requisites
;;
--- 97,105 ----
;;
;; Which version of todo-mode.el does this documentation refer to?
;;
! ;; $Id: todo-mode.el,v 1.51 2002/05/28 19:16:00 steve Exp $
! ;;
! ;; Commentary marked with `[srb]' was added by Steve Berman. [srb]
;;
;; Pre-Requisites
;;
***************
*** 106,150 ****
;;
;; time-stamp
;; easymenu
;;
;; Operation
;;
;; You will have the following facilities available:
;;
! ;; M-x todo-show will enter the todo list screen, here type
;;
;; + to go to next category
;; - to go to previous category
! ;; d to file the current entry, including a
! ;; comment and timestamp
;; e to edit the current entry
;; E to edit a multi-line entry
;; f to file the current entry, including a
;; comment and timestamp
! ;; i to insert a new entry, with prefix, omit category
;; I to insert a new entry at current cursor position
;; j jump to category
;; k to kill the current entry
;; l to lower the current entry's priority
;; n for the next entry
;; p for the previous entry
;; P print
;; q to save the list and exit the buffer
;; r to raise the current entry's priority
;; s to save the list
;; S to save the list of top priorities
;; t show top priority items for each category
;;
! ;; When you add a new entry, you are asked for the text and then
! ;; for the category. I for example have categories for things
! ;; that I want to do in the office (like mail my mum), that I
! ;; want to do in town (like buy cornflakes) and things I want to
;; do at home (move my suitcases). The categories can be
;; selected with the cursor keys and if you type in the name of a
;; category which didn't exist before, an empty category of the
;; desired name will be added and filled with the new entry.
! ;;
! ;; Configuration
;;
;; Variable todo-prefix
;;
--- 108,190 ----
;;
;; time-stamp
;; easymenu
+ ;; cl (unless Emacs 21.1 or later) [srb]
;;
;; Operation
;;
;; You will have the following facilities available:
;;
! ;; M-x todo-show will enter todo-mode and display the todo
! ;; list screen. The functions of this mode
! ;; can be invoked by clicking on an item from
! ;; the "Todo" menu in the menubar, or by typing:
;;
;; + to go to next category
;; - to go to previous category
! ;; A to add a new todo category [srb]
! ;; c to display a clickable list of all todo categories [srb]
! ;; C to change the string separating entries in
! ;; the current category [srb]
! ;; d to display the filed entries of the current category [srb]
! ;; D to delete the current category [srb]
;; e to edit the current entry
;; E to edit a multi-line entry
;; f to file the current entry, including a
;; comment and timestamp
! ;; F to file the current entry under its category,
! ;; including a comment and timestamp [srb]
! ;; h to toggle display of item prefix or timestamp and initials [srb]
! ;; i to insert a new entry, with prefix, solicit category
;; I to insert a new entry at current cursor position
;; j jump to category
;; k to kill the current entry
;; l to lower the current entry's priority
+ ;; m to move the current entry to another category
;; n for the next entry
;; p for the previous entry
;; P print
;; q to save the list and exit the buffer
;; r to raise the current entry's priority
+ ;; R to rename the current category [srb]
;; s to save the list
;; S to save the list of top priorities
;; t show top priority items for each category
+ ;; X to change the todo prefix string [srb]
;;
! ;; When you add a new entry, you are asked for the text and
! ;; optionally for the category. I for example have categories for
! ;; things that I want to do in the office (like mail my mum), that
! ;; I want to do in town (like buy cornflakes) and things I want to
;; do at home (move my suitcases). The categories can be
;; selected with the cursor keys and if you type in the name of a
;; category which didn't exist before, an empty category of the
;; desired name will be added and filled with the new entry.
! ;;
! ;; Todo list items can be edited as normal text, and they can be
! ;; reprioritized by moving them up and down within their category.
! ;; Items can also be moved from one category to another. If you
! ;; want to remove a todo list entry, you can either permanently
! ;; delete it or file it. You can file it either in a separate
! ;; file for done items (see below) or within its category. In the
! ;; latter case, it is displayed in separate window below the
! ;; unfiled items in that category. The buffer for filed items has
! ;; its own mode, for which a restricted subset of todo-mode
! ;; commands is available. [srb]
! ;;
! ;; Todo categories can be displayed in a buffer as an
! ;; alphabetized list, and clicking or pressing the return key on
! ;; a category name in the list pops up the buffer of that
! ;; category. Categories can be renamed and, as long as they are
! ;; empty (i.e., contain neither unfiled nor filed items), can
! ;; also be deleted. [srb]
! ;;
! ;; Configuration [srb for the following three sentences]
! ;;
! ;; Many of todo-mode's options can be customized by the user;
! ;; to do so click on "Customize" in the "Todo" menu. If you save
! ;; your customizations for future sessions, they will be loaded
! ;; the next time you invoke todo-mode. The customizable variables
! ;; include:
;;
;; Variable todo-prefix
;;
***************
*** 175,180 ****
--- 215,222 ----
;; Carsten also writes that that *changing* the prefix after the
;; todo list is already established is not as simple as changing
;; the variable - the todo files have to be changed by hand.
+ ;; (The command `todo-change-todo-prefix' has now been added to
+ ;; do this. [srb])
;;
;; Variable todo-file-do
;;
***************
*** 223,228 ****
--- 265,281 ----
;; corrected some of my awful coding and pointed me towards some
;; good reading. Thanks Trey!
;;
+ ;; Variable todo-item-separator [srb]
+ ;;
+ ;; This variable stores the string used to separate entries in
+ ;; the todo list. It may improve the readability of a crowded
+ ;; category, and may vary between categories to visually
+ ;; distinguish them. To insure proper behavior it must begin and
+ ;; end with a newline (hence minimally "\n", the default value)
+ ;; and not contain alphanumeric characters. Its value can also
+ ;; be changed within a todo-category buffer by
+ ;; `M-x todo-change-item-sep-in-cat'.
+ ;;
;; Things to do
;;
;; These originally were my ideas, but now also include all the
***************
*** 230,237 ****
;;
;; o Fancy fonts for todo/top-priority buffer
;; o Remove todo-prefix option in todo-top-priorities
! ;; o Rename category
! ;; o Move entry from one category to another one
;; o Entries which both have the generic */* prefix and a
;; "deadline" entry which are understood by diary, indicating
;; an event (unless marked by &)
--- 283,290 ----
;;
;; o Fancy fonts for todo/top-priority buffer
;; o Remove todo-prefix option in todo-top-priorities
! ;; o Rename category [Done [srb]]
! ;; o Move entry from one category to another one [Done [srb]]
;; o Entries which both have the generic */* prefix and a
;; "deadline" entry which are understood by diary, indicating
;; an event (unless marked by &)
***************
*** 261,272 ****
;;
;; Oliver Seidel
;; (Lessingstr. 8, 65760 Eschborn, Federal Republic of Germany)
;;; Code:
(require 'time-stamp)
-
;; User-configurable variables:
(defgroup todo nil
--- 314,325 ----
;;
;; Oliver Seidel
;; (Lessingstr. 8, 65760 Eschborn, Federal Republic of Germany)
+ ;;
;;; Code:
(require 'time-stamp)
;; User-configurable variables:
(defgroup todo nil
***************
*** 306,311 ****
--- 359,379 ----
"*TODO Edit mode hooks."
:type 'hook
:group 'todo)
+ (defcustom todo-categories-mode-hook nil
+ "*TODO Categories mode hooks."
+ :type 'hook
+ :group 'todo)
+ (defcustom todo-filed-mode-hook nil
+ "*TODO Filed mode hooks."
+ :type 'hook
+ :group 'todo)
+ (defcustom todo-item-separator "\n"
+ "*String for separating todo entries within a category.
+ To insure proper behavior must begin and end with a newline and not contain
+ alphanumeric characters. Can also be changed within a todo-category buffer
+ by \\[todo-change-item-sep-in-cat]."
+ :type 'string
+ :group 'todo)
(defcustom todo-insert-threshold 0
"*TODO mode insertion accuracy.
***************
*** 322,329 ****
window."
:type 'integer
:group 'todo)
- (defvar todo-edit-buffer " *TODO Edit*"
- "TODO Edit buffer name.")
(defcustom todo-file-top "~/.todo-top"
"*TODO mode top priorities file.
--- 390,395 ----
***************
*** 347,354 ****
:type 'integer
:group 'todo)
(defcustom todo-remove-separator t
! "*Non-nil to remove category separators in\
! \\[todo-top-priorities] and \\[todo-print]."
:type 'boolean
:group 'todo)
(defcustom todo-save-top-priorities-too t
--- 413,419 ----
:type 'integer
:group 'todo)
(defcustom todo-remove-separator t
! "*Non-nil to remove category separators in output of \\[todo-top-priorities] and \\[todo-print]."
:type 'boolean
:group 'todo)
(defcustom todo-save-top-priorities-too t
***************
*** 391,396 ****
--- 456,464 ----
"Old variable for holding the TODO categories.
Use `todo-categories' instead.")
+ (defvar todo-filed-categories todo-categories
+ "Variable for TODO categories in todo-filed-mode.")
+
(defvar todo-previous-line 0
"Previous line asked about.")
***************
*** 402,443 ****
(suppress-keymap map t)
(define-key map "+" 'todo-forward-category)
(define-key map "-" 'todo-backward-category)
! (define-key map "d" 'todo-file-item) ;done/delete
(define-key map "e" 'todo-edit-item)
(define-key map "E" 'todo-edit-multiline)
(define-key map "f" 'todo-file-item)
(define-key map "i" 'todo-insert-item)
(define-key map "I" 'todo-insert-item-here)
(define-key map "j" 'todo-jump-to-category)
(define-key map "k" 'todo-delete-item)
(define-key map "l" 'todo-lower-item)
(define-key map "n" 'todo-forward-item)
(define-key map "p" 'todo-backward-item)
(define-key map "P" 'todo-print)
(define-key map "q" 'todo-quit)
(define-key map "r" 'todo-raise-item)
(define-key map "s" 'todo-save)
(define-key map "S" 'todo-save-top-priorities)
(define-key map "t" 'todo-top-priorities)
map)
"TODO mode keymap.")
(defvar todo-category-number 0 "TODO category number.")
(defvar todo-tmp-buffer-name " *todo tmp*")
(defvar todo-category-sep (make-string 75 ?-)
"Category separator.")
(defvar todo-category-beg " --- "
"Category start separator to be prepended onto category name.")
(defvar todo-category-end "--- End"
"Separator after a category.")
(defvar todo-header "-*- mode: todo; "
"Header of todo files.")
;; ---------------------------------------------------------------------------
(defun todo-category-select ()
--- 470,560 ----
(suppress-keymap map t)
(define-key map "+" 'todo-forward-category)
(define-key map "-" 'todo-backward-category)
! (define-key map "A" 'todo-add-category)
! (define-key map "c" 'todo-display-categories)
! (define-key map "C" 'todo-change-item-sep-in-cat)
! (define-key map "d" 'todo-display-filed-items-under-cat)
! (define-key map "D" 'todo-delete-category)
(define-key map "e" 'todo-edit-item)
(define-key map "E" 'todo-edit-multiline)
(define-key map "f" 'todo-file-item)
+ (define-key map "F" 'todo-file-item-under-cat)
+ (define-key map "h" 'todo-toggle-item-header)
(define-key map "i" 'todo-insert-item)
(define-key map "I" 'todo-insert-item-here)
(define-key map "j" 'todo-jump-to-category)
(define-key map "k" 'todo-delete-item)
(define-key map "l" 'todo-lower-item)
+ (define-key map "m" 'todo-move-item)
(define-key map "n" 'todo-forward-item)
(define-key map "p" 'todo-backward-item)
(define-key map "P" 'todo-print)
(define-key map "q" 'todo-quit)
(define-key map "r" 'todo-raise-item)
+ (define-key map "R" 'todo-rename-category)
(define-key map "s" 'todo-save)
(define-key map "S" 'todo-save-top-priorities)
(define-key map "t" 'todo-top-priorities)
+ (define-key map "X" 'todo-change-todo-prefix)
map)
"TODO mode keymap.")
+ (defvar todo-filed-mode-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map todo-mode-map)
+ (suppress-keymap map t)
+ (define-key map "C" 'todo-change-item-sep-in-cat)
+ (define-key map "e" 'todo-edit-item)
+ (define-key map "E" 'todo-edit-multiline)
+ (define-key map "f" 'todo-file-item)
+ (define-key map "h" 'todo-toggle-item-header)
+ (define-key map "k" 'todo-delete-item)
+ (define-key map "n" 'todo-forward-item)
+ (define-key map "p" 'todo-backward-item)
+ (define-key map "q" 'todo-kill-buffer)
+ (define-key map "s" 'todo-save)
+ map)
+ "TODO filed mode keymap.")
+
+ (defvar todo-categories-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "q" 'todo-kill-buffer)
+ (define-key map [mouse-2] 'todo-category-click)
+ (define-key map "\r" 'todo-category-return)
+ map)
+ "TODO categories mode keymap.")
+
(defvar todo-category-number 0 "TODO category number.")
(defvar todo-tmp-buffer-name " *todo tmp*")
+ (defvar todo-edit-buffer " *TODO Edit*"
+ "TODO Edit buffer name.")
+
+ (defvar todo-categories-buffer "Todo Categories"
+ "TODO categories buffer name.")
+
+ (defvar todo-filed-buffer "Filed items:"
+ "Name of buffer displaying filed items in the current category.")
+
(defvar todo-category-sep (make-string 75 ?-)
"Category separator.")
(defvar todo-category-beg " --- "
"Category start separator to be prepended onto category name.")
+ (defvar todo-category-filed "--- Filed"
+ "Separator for filed category items, inserted before todo-category-end.")
+
(defvar todo-category-end "--- End"
"Separator after a category.")
(defvar todo-header "-*- mode: todo; "
"Header of todo files.")
+ (defvar todo-buffer-read-only t ; set to nil to make todo-mode buffer writable
+ "For setting read-only status of todo-mode. Default is read-only.")
+
;; ---------------------------------------------------------------------------
(defun todo-category-select ()
***************
*** 452,461 ****
(concat "^"
(regexp-quote (concat todo-prefix todo-category-beg name))
"$"))
! (let ((begin (1+ (line-end-position))))
! (search-forward-regexp (concat "^" todo-category-end))
! (narrow-to-region begin (line-beginning-position))
! (goto-char (point-min)))))
(defalias 'todo-cat-slct 'todo-category-select)
(defun todo-forward-category ()
--- 569,593 ----
(concat "^"
(regexp-quote (concat todo-prefix todo-category-beg name))
"$"))
! ;; display only unfiled items
! (let* ((begin (1+ (line-end-position)))
! (end0 (progn
! (re-search-forward (concat "^" todo-category-end))
! (match-beginning 0)))
! (filed-beg (re-search-backward
! (concat "^" todo-category-filed "$") begin t))
! (end (or filed-beg end0)))
! (narrow-to-region begin end)
! (goto-char (point-min)))
! ;; set the value of todo-item-separator to the actual string used in
! ;; this category (needed e.g. for todo-change-item-sep-in-cat)
! (setq todo-item-separator (save-excursion
! (goto-char (point-min))
! (if (re-search-forward
! (concat "\\(\n[^A-Ba-z0-9]*\\)"
! (regexp-quote todo-prefix)) nil t)
! (match-string 1)
! "\n")))))
(defalias 'todo-cat-slct 'todo-category-select)
(defun todo-forward-category ()
***************
*** 477,483 ****
(defun todo-backward-item ()
"Select previous entry of TODO list."
(interactive)
! (search-backward-regexp (concat "^" (regexp-quote todo-prefix)) nil t)
(message ""))
(defalias 'todo-cmd-prev 'todo-backward-item)
--- 609,621 ----
(defun todo-backward-item ()
"Select previous entry of TODO list."
(interactive)
! (if (or (looking-at (concat "^" (regexp-quote todo-prefix)))
! (todo-item-separator-p)
! (eobp))
! (search-backward-regexp (concat "^" (regexp-quote todo-prefix)) nil t)
! ;; go up to previous item also from anywhere within the current item
! (goto-char (todo-item-start))
! (search-backward-regexp (concat "^" (regexp-quote todo-prefix)) nil t))
(message ""))
(defalias 'todo-cmd-prev 'todo-backward-item)
***************
*** 488,493 ****
--- 626,633 ----
(end-of-line)
(search-forward-regexp (concat "^" (regexp-quote todo-prefix))
nil 'goto-end count)
+ ;; empty last line is not an item
+ (if (and (eobp) (looking-at "^$")) (backward-char 1))
(beginning-of-line)
(message ""))
(defalias 'todo-cmd-next 'todo-forward-item)
***************
*** 511,595 ****
(defalias 'todo-cmd-done 'todo-quit)
(defun todo-edit-item ()
! "Edit current TODO list entry."
(interactive)
! (let ((item (todo-item-string)))
! (if (todo-string-multiline-p item)
! (todo-edit-multiline)
! (let ((new (read-from-minibuffer "Edit: " item)))
! (todo-remove-item)
! (insert new "\n")
! (todo-backward-item)
! (message "")))))
(defalias 'todo-cmd-edit 'todo-edit-item)
(defun todo-edit-multiline ()
! "Set up a buffer for editing a multiline TODO list entry."
(interactive)
! (let ((buffer-name (generate-new-buffer-name todo-edit-buffer)))
! (switch-to-buffer
! (make-indirect-buffer
! (file-name-nondirectory todo-file-do) buffer-name))
! (message "To exit, simply kill this buffer and return to list.")
! (todo-edit-mode)
! (narrow-to-region (todo-item-start) (todo-item-end))))
;;;###autoload
(defun todo-add-category (cat)
"Add new category CAT to the TODO list."
(interactive "sCategory: ")
(save-window-excursion
(setq todo-categories (cons cat todo-categories))
(find-file todo-file-do)
! (widen)
! (goto-char (point-min))
! (let ((posn (search-forward "-*- mode: todo; " 17 t)))
! (if (not (null posn)) (goto-char posn))
! (if (equal posn nil)
! (progn
! (insert "-*- mode: todo; \n")
! (forward-char -1))
! (kill-line)))
! (insert (format "todo-categories: %S; -*-" todo-categories))
! (forward-char 1)
! (insert (format "%s%s%s\n%s\n%s %s\n"
! todo-prefix todo-category-beg cat
! todo-category-end
! todo-prefix todo-category-sep)))
! 0)
;;;###autoload
(defun todo-add-item-non-interactively (new-item category)
"Insert NEW-ITEM in TODO list as a new entry in CATEGORY."
(save-excursion
(todo-show))
! (save-excursion
! (if (string= "" category)
! (setq category (nth todo-category-number todo-categories)))
! (let ((cat-exists (member category todo-categories)))
! (setq todo-category-number
! (if cat-exists
! (- (length todo-categories) (length cat-exists))
! (todo-add-category category))))
! (todo-show)
! (setq todo-previous-line 0)
! (let ((top 1)
! (bottom (1+ (count-lines (point-min) (point-max)))))
! (while (> (- bottom top) todo-insert-threshold)
! (let* ((current (/ (+ top bottom) 2))
! (answer (if (< current bottom)
! (todo-more-important-p current) nil)))
! (if answer
! (setq bottom current)
! (setq top (1+ current)))))
! (setq top (/ (+ top bottom) 2))
! ;; goto-line doesn't have the desired behavior in a narrowed buffer
! (goto-char (point-min))
! (forward-line (1- top)))
! (insert new-item "\n")
! (todo-backward-item)
! (todo-save)
! (message "")))
;;;###autoload
(defun todo-insert-item (arg)
--- 651,910 ----
(defalias 'todo-cmd-done 'todo-quit)
(defun todo-edit-item ()
! "Edit current unfiled or filed TODO list entry."
(interactive)
! (if (todo-item-string-p)
! (let ((item (todo-item-string)))
! (if (todo-string-multiline-p item)
! (todo-edit-multiline)
! (let* ((buffer-read-only nil)
! ;; cursor position within item prior to editing
! (here (- (point) (todo-item-start) -1))
! (new (read-from-minibuffer
! "Edit: " (cons item here)))) ; start editing at point
! (todo-remove-item)
! (insert new todo-item-separator)
! (todo-backward-item)
! ;; make cursor position after editing the same as before editing
! (forward-char (1- here))
! (message ""))))
! (error "No TODO list entry to edit")))
(defalias 'todo-cmd-edit 'todo-edit-item)
(defun todo-edit-multiline ()
! "Set up a buffer for editing an unfiled or filed multiline TODO list entry."
(interactive)
! (if (todo-item-string-p)
! (let ((buffer-name (generate-new-buffer-name todo-edit-buffer))
! (here (point))) ; to enable editing filed multiline items
! (switch-to-buffer
! (make-indirect-buffer
! (file-name-nondirectory todo-file-do) buffer-name))
! (todo-edit-mode)
! (message "To finish editing and return to TODO item list, type q.")
! (widen) ; in case the item is filed
! (goto-char here)
! ;; This bit of code insures that, if the last unfiled or filed item
! ;; in the category is inserted into the editing buffer, the
! ;; todo-category-filed or todo-category-end string, respectively,
! ;; is not displayed with it.
! (let* ((begin (save-excursion
! (search-backward-regexp
! (concat "^" (regexp-quote
! (concat todo-prefix todo-category-beg))))
! (1+ (line-end-position))))
! (end (save-excursion
! (re-search-forward (concat "^" todo-category-end))
! (match-beginning 0)))
! (filed-beg (save-excursion
! (if (re-search-forward
! (concat "^" todo-category-filed "$") end t)
! (match-beginning 0))))
! (filed-end (save-excursion
! (if (re-search-backward
! (concat "^" todo-category-filed "$") begin t)
! (match-end 0)))))
! (cond (filed-end (narrow-to-region filed-end end))
! (filed-beg (narrow-to-region begin filed-beg))
! (t (narrow-to-region begin end))))
! (narrow-to-region (todo-item-start) (todo-item-end)))
! (error "No TODO list entry to edit")))
;;;###autoload
(defun todo-add-category (cat)
"Add new category CAT to the TODO list."
(interactive "sCategory: ")
+ (if (string= "" cat) (error "A category name must be supplied"))
+ (if (member cat todo-categories)
+ (error "Category already exists -- choose another name"))
(save-window-excursion
(setq todo-categories (cons cat todo-categories))
(find-file todo-file-do)
! (let ((buffer-read-only nil))
! (widen)
! (goto-char (point-min))
! (let ((posn (search-forward "-*- mode: todo; " 17 t)))
! (if (not (null posn)) (goto-char posn))
! (if (equal posn nil)
! (progn
! (insert "-*- mode: todo; \n")
! (forward-char -1))
! (kill-line)))
! (insert (format "todo-categories: %S; -*-" todo-categories))
! (forward-char 1)
! (insert (format "%s%s%s\n%s\n%s %s\n"
! todo-prefix todo-category-beg cat
! todo-category-end
! todo-prefix todo-category-sep))))
! ;; show the newly added category
! (setq todo-category-number 0)
! (todo-category-select))
!
! (defun todo-rename-category (new)
! "Rename current todo category."
! (interactive "sNew category name: ")
! (let ((cat (nth todo-category-number todo-categories))
! (vec (vconcat todo-categories))
! (buffer-read-only nil))
! (if (string= "" new) (error "A category name must be supplied"))
! (aset vec todo-category-number new)
! (setq todo-categories (append vec nil))
! (save-excursion
! (widen)
! (search-backward (concat todo-prefix todo-category-beg))
! (goto-char (match-end 0))
! (when (looking-at (regexp-quote cat))
! (replace-match new t))
! (goto-char (point-min))
! (when (looking-at (regexp-quote todo-header))
! (goto-char (match-end 0))
! (kill-line)
! (insert (format "todo-categories: %S; -*-" todo-categories)))
! (setq mode-line-buffer-identification
! (concat "Category: " (format "%18s" new)))))
! (todo-category-select))
!
! (defun todo-delete-category ()
! "Delete current TODO category if it contains no filed or unfiled items."
! (interactive)
! (widen)
! (let* ((cat (nth todo-category-number todo-categories))
! (begin0 (search-backward (concat todo-prefix todo-category-beg cat)))
! (begin (1+ (match-end 0)))
! (end (progn
! (re-search-forward (concat "^" todo-category-end "$"))
! (match-beginning 0)))
! (cat-end (1+ (search-forward
! (concat todo-prefix " " todo-category-sep))))
! (filed-beg (re-search-backward
! (concat "^" todo-category-filed "$") begin t))
! (filed-end (if (not (null filed-beg)) (1+ (match-end 0))))
! (buffer-read-only nil))
! ;; A deletable category must contain neither unfiled items...
! (cond ((or (and (not (null filed-beg)) (not (equal begin filed-beg)))
! (and (null filed-beg) (not (equal begin end))))
! (todo-category-select)
! (error "Cannot delete nonempty category"))
! ;; ...nor filed items.
! ((and (not (null filed-beg)) (not (equal filed-end end)))
! (todo-category-select)
! (todo-display-filed-items-under-cat)
! (error "Cannot delete category with filed items"))
! ;; category is empty, so request deletion confirmation
! (t (if (equal filed-end end) (setq begin filed-end))
! (todo-category-select)
! (if (y-or-n-p (concat "Permanently remove '" cat "'? "))
! (progn
! (widen)
! (kill-region begin0 cat-end)
! (goto-char (point-min))
! (when (looking-at (regexp-quote todo-header))
! (setq todo-categories
! (delete cat todo-categories))
! (goto-char (match-end 0))
! (kill-line)
! (insert (format "todo-categories: %S; -*-"
! todo-categories))
! (todo-category-select)
! (message "Deleted category \"%s\"" cat)))
! (todo-category-select))))))
!
! (defun todo-display-categories ()
! "Display clickable alphabetical list of TODO categories.
! Click or type RET on a category name to go to it."
! (interactive)
! (let ((cat-list todo-categories)
! (buffer-name (concat "*" todo-categories-buffer "*")))
! (with-current-buffer (get-buffer-create buffer-name)
! (pop-to-buffer (current-buffer))
! (let ((buffer-read-only nil))
! (erase-buffer)
! (kill-all-local-variables)
! ;; insert the category names as clickable text
! (while cat-list
! (setq cat (car cat-list))
! (insert cat)
! (add-text-properties (line-beginning-position) (line-end-position)
! (list 'mouse-face 'highlight))
! (insert "\n")
! (setq cat-list (cdr cat-list)))
! ;; display categories in case-insensitive alphabetical order
! (make-local-variable 'sort-fold-case)
! (let ((begin (point-min))
! (end (point-max))
! (sort-fold-case t))
! (sort-lines nil begin end))
! (goto-char (point-min)))
! (todo-categories-mode)
! ;; to enable todo-category-click under tty (but useless if the tty
! ;; doesn't emulate an xterm with mouse support, e.g. Linux console
! ;; without gpm enabled)
! (if (null window-system) (funcall 'xterm-mouse-mode 1))
! (message "Click or type RET on a category to go to it. Type \"q\" to quit."))))
;;;###autoload
(defun todo-add-item-non-interactively (new-item category)
"Insert NEW-ITEM in TODO list as a new entry in CATEGORY."
(save-excursion
(todo-show))
! (if (string= "" category)
! (setq category (nth todo-category-number todo-categories)))
! (let ((cat-exists (member category todo-categories))
! (buffer-read-only nil))
! (setq todo-category-number
! (if cat-exists
! (- (length todo-categories) (length cat-exists))
! (todo-add-category category)))
! ;; (todo-show) is needed here for todo-item-move, but with a new
! ;; category produces a `let: Wrong type argument: integerp, "^J"'
! ;; error, the source of which currently escapes me.
! (unless (eq (point-min) (point-max))
! (todo-show))
! ;; need to make separator "\n" to calculate priority,
! ;; so save current separator for later reinsertion
! (let ((old-sep (save-excursion
! (goto-char (point-min))
! (if (re-search-forward
! (concat "\\(\n[^A-Ba-z0-9]*\\)"
! (regexp-quote todo-prefix)) nil t)
! (match-string 1)
! "\n"))))
! (unless (eq (point-min) (point-max))
! (funcall 'todo-change-item-sep-in-cat "\n"))
! ;; If the category is still empty, just insert the new item...
! (if (eq 0 (todo-count-items))
! (progn
! (insert new-item todo-item-separator)
! ;; Without the following, `Wrong type argument' errors result,
! ;; for reasons I currently don't understand. (However, it
! ;; also causes todo-item-move to fail if it is used to create
! ;; a new category, so for now I've had to exclude that
! ;; possibility from todo-item-move.)
! (todo-jump-to-category-noninteractively category))
! ;; ...otherwise, calculate the new item's priority and then insert it.
! (setq todo-previous-line 0)
! (let ((top 1)
! (bottom (1+ (todo-count-items)))
! (last 1))
! (while (> (- bottom top) todo-insert-threshold)
! (let* ((current (/ (+ top bottom) 2))
! (answer (if (< current bottom)
! (todo-more-important-p current) nil)))
! (if answer
! (setq bottom current)
! (setq top (1+ current)))
! (setq last current)))
! (if (todo-more-important-p last)
! (insert new-item todo-item-separator)
! (goto-char (1+ (todo-item-end)))
! (insert new-item todo-item-separator))))
! (todo-backward-item)
! (funcall 'todo-change-item-sep-in-cat old-sep)
! ; (todo-save)
! (message ""))))
! ;; todo-show puts cursor at (point-min) rather than leaving it at the
! ;; beginning of the new item
! ; (todo-show))))
;;;###autoload
(defun todo-insert-item (arg)
***************
*** 597,633 ****
With a prefix argument solicit the category, otherwise use the current
category."
(interactive "P")
! (save-excursion
! (if (not (string-equal mode-name "TODO")) (todo-show))
! (let* ((new-item (concat todo-prefix " "
! (read-from-minibuffer
! "New TODO entry: "
! (if todo-entry-prefix-function
! (funcall todo-entry-prefix-function)))))
! (categories todo-categories)
! (history (cons 'categories (1+ todo-category-number)))
! (current-category (nth todo-category-number todo-categories))
! (category
! (if arg
! current-category
(completing-read (concat "Category [" current-category "]: ")
(todo-category-alist) nil nil nil
! history current-category))))
! (todo-add-item-non-interactively new-item category))))
!
(defalias 'todo-cmd-inst 'todo-insert-item)
(defun todo-insert-item-here ()
"Insert new TODO list entry under the cursor."
! (interactive "")
! (save-excursion
! (if (not (string-equal mode-name "TODO")) (todo-show))
! (let* ((new-item (concat todo-prefix " "
! (read-from-minibuffer
! "New TODO entry: "
! (if todo-entry-prefix-function
! (funcall todo-entry-prefix-function))))))
! (insert (concat new-item "\n")))))
(defun todo-more-important-p (line)
"Ask whether entry is more important than the one at LINE."
--- 912,972 ----
With a prefix argument solicit the category, otherwise use the current
category."
(interactive "P")
! (if (not (string-equal mode-name "TODO")) (todo-show))
! (let* ((new-item (concat todo-prefix " "
! (read-from-minibuffer
! "New TODO entry: "
! (if todo-entry-prefix-function
! (funcall todo-entry-prefix-function)))))
! (categories todo-categories)
! (history (cons 'categories (1+ todo-category-number)))
! (current-category (nth todo-category-number todo-categories))
! (category
! (if arg
(completing-read (concat "Category [" current-category "]: ")
(todo-category-alist) nil nil nil
! history current-category)
! current-category)))
! (todo-add-item-non-interactively new-item category)))
(defalias 'todo-cmd-inst 'todo-insert-item)
(defun todo-insert-item-here ()
"Insert new TODO list entry under the cursor."
! (interactive)
! (if (not (string-equal mode-name "TODO")) (todo-show))
! (let* ((new-item (concat todo-prefix " "
! (read-from-minibuffer
! "New TODO entry: "
! (if todo-entry-prefix-function
! (funcall todo-entry-prefix-function)))))
! (buffer-read-only nil))
! ;; If the cursor is on the first item in the category, insert this
! ;; item immediately above, making it the new first item, and add
! ;; an item separator below it; if the cursor is on any other item,
! ;; insert this item directly below, putting a separator in between;
! ;; if the cursor is between two items (i.e. on a separator string),
! ;; insert this item in between. Puts the cursor at the beginning of
! ;; the new item.
! (cond ((eq (point-min) (point-max)) ; category has no unfiled items
! (insert (concat new-item todo-item-separator)))
! ((todo-first-in-category-p)
! (goto-char (point-min))
! (insert (concat new-item todo-item-separator))
! (goto-char (point-min)))
! ((and (todo-item-string-p) (not (todo-last-in-category-p)))
! (goto-char (todo-item-end))
! (insert (concat todo-item-separator new-item)))
! ((and (todo-item-separator-p) (not (eobp)))
! (re-search-forward (regexp-quote todo-prefix) nil 1 nil)
! (goto-char (1- (match-beginning 0)))
! (insert (concat "\n" new-item todo-item-separator))
! (delete-backward-char 1))
! ((or (todo-last-in-category-p) (eobp))
! (goto-char (point-max))
! (if (looking-at "^$") (delete-backward-char 1))
! (forward-line)
! (insert (concat todo-item-separator new-item "\n")))))
! (goto-char (todo-item-start)))
(defun todo-more-important-p (line)
"Ask whether entry is more important than the one at LINE."
***************
*** 635,641 ****
(progn
(setq todo-previous-line line)
(goto-char (point-min))
! (forward-line (1- todo-previous-line))
(let ((item (todo-item-string-start)))
(setq todo-previous-answer
(y-or-n-p (concat "More important than '" item "'? "))))))
--- 974,980 ----
(progn
(setq todo-previous-line line)
(goto-char (point-min))
! (todo-forward-item (1- todo-previous-line))
(let ((item (todo-item-string-start)))
(setq todo-previous-answer
(y-or-n-p (concat "More important than '" item "'? "))))))
***************
*** 645,717 ****
(defun todo-delete-item ()
"Delete current TODO list entry."
(interactive)
! (if (> (count-lines (point-min) (point-max)) 0)
(let* ((todo-entry (todo-item-string-start))
! (todo-answer (y-or-n-p (concat "Permanently remove '"
! todo-entry "'? "))))
! (if todo-answer
! (progn
! (todo-remove-item)
! (todo-backward-item)))
! (message ""))
(error "No TODO list entry to delete")))
(defalias 'todo-cmd-kill 'todo-delete-item)
(defun todo-raise-item ()
"Raise priority of current entry."
(interactive)
! (if (> (count-lines (point-min) (point)) 0)
! (let ((item (todo-item-string)))
! (todo-remove-item)
! (todo-backward-item)
! (save-excursion
! (insert item "\n"))
! (message ""))
! (error "No TODO list entry to raise")))
(defalias 'todo-cmd-rais 'todo-raise-item)
(defun todo-lower-item ()
"Lower priority of current entry."
(interactive)
! (if (> (count-lines (point) (point-max)) 1)
;; Assume there is a final newline
! (let ((item (todo-item-string)))
(todo-remove-item)
! (todo-forward-item)
! (save-excursion
! (insert item "\n"))
(message ""))
(error "No TODO list entry to lower")))
(defalias 'todo-cmd-lowr 'todo-lower-item)
! (defun todo-file-item (&optional comment)
! "File the current TODO list entry away, annotated with an optional COMMENT."
! (interactive "sComment: ")
(or (> (count-lines (point-min) (point-max)) 0)
(error "No TODO list entry to file away"))
! (let ((time-stamp-format todo-time-string-format))
! (if (and comment (> (length comment) 0))
(progn
! (goto-char (todo-item-end))
! (insert
! (if (save-excursion (beginning-of-line)
! (looking-at (regexp-quote todo-prefix)))
! " "
! "\n\t")
! "(" comment ")")))
(goto-char (todo-item-end))
! (insert " [" (nth todo-category-number todo-categories) "]")
! (goto-char (todo-item-start))
! (let ((temp-point (point)))
! (if (looking-at (regexp-quote todo-prefix))
! (replace-match (time-stamp-string))
! ;; Standard prefix -> timestamp
! ;; Else prefix non-standard item start with timestamp
! (insert (time-stamp-string)))
! (append-to-file temp-point (1+ (todo-item-end)) todo-file-done)
! (delete-region temp-point (1+ (todo-item-end))))
! (todo-backward-item)
! (message "")))
;; ---------------------------------------------------------------------------
--- 984,1336 ----
(defun todo-delete-item ()
"Delete current TODO list entry."
(interactive)
! (if (and (todo-item-string-p)
! (> (count-lines (point-min) (point-max)) 0))
(let* ((todo-entry (todo-item-string-start))
! (todo-answer (y-or-n-p (concat "Permanently remove '"
! todo-entry "'? ")))
! (buffer-read-only nil))
! (if todo-answer
! (if (todo-last-in-category-p)
! (progn
! (todo-remove-item)
! (if (search-backward todo-item-separator (point-min) t)
! (progn
! (delete-region (point) (point-max))
! (newline)
! (todo-backward-item))))
! (todo-remove-item)))
! ;; uncomment to make the cursor move to the item immediately
! ;; preceding the deleted item
! ; (todo-backward-item)))
! (message ""))
(error "No TODO list entry to delete")))
(defalias 'todo-cmd-kill 'todo-delete-item)
(defun todo-raise-item ()
"Raise priority of current entry."
(interactive)
! (if (and (> (count-lines (point-min) (point)) 0)
! (and (todo-item-string-p) (not (eobp)))
! (not (todo-first-in-category-p)))
! (let ((item (todo-item-string))
! (buffer-read-only nil))
! (todo-remove-item)
! (todo-backward-item)
! (if (todo-last-in-category-p)
! (save-excursion
! (goto-char (todo-item-end))
! (newline)
! ;; don't leave item separator dangling after the last item
! (let ((bound (save-excursion (search-backward todo-prefix)
! (match-end 0))))
! (if (search-backward todo-item-separator bound t)
! nil)
! (delete-region (point) (point-max))
! (newline))))
! (save-excursion (insert item todo-item-separator))
! (message ""))
! (error "No TODO list entry to raise")))
(defalias 'todo-cmd-rais 'todo-raise-item)
(defun todo-lower-item ()
"Lower priority of current entry."
(interactive)
! (if (and (> (count-lines (point) (point-max)) 1)
! (todo-item-string-p) (not (todo-last-in-category-p)))
;; Assume there is a final newline
! (let ((item (todo-item-string))
! (buffer-read-only nil))
(todo-remove-item)
! (if (todo-last-in-category-p)
! (goto-char (point-max))
! (todo-forward-item))
! (if (eobp) (progn (delete-backward-char 1)
! (insert todo-item-separator item))
! (insert item todo-item-separator)
! (re-search-backward (regexp-quote todo-prefix)))
! (if (equal (point) (point-max))
! (progn (auto-fill-mode nil) (newline) (forward-line -1)
! (auto-fill-mode t)))
(message ""))
(error "No TODO list entry to lower")))
(defalias 'todo-cmd-lowr 'todo-lower-item)
! (defun todo-file-item ()
! "File the current TODO list entry away, annotated with an optional comment."
! (interactive)
(or (> (count-lines (point-min) (point-max)) 0)
(error "No TODO list entry to file away"))
! (if (todo-item-string-p)
! (if (y-or-n-p "Put this item in the done items file? ")
! (let ((time-stamp-format todo-time-string-format)
! (cat (nth todo-category-number todo-categories))
! (bare-item (buffer-substring
! (progn
! (goto-char (todo-item-start))
! (search-forward todo-initials))
! (todo-item-end)))
! (old-timestamp (buffer-substring
! (1+ (progn (goto-char (todo-item-start))
! (search-forward todo-prefix)))
! (search-forward todo-initials)))
! (fill-prefix " ")
! (buffer-read-only nil))
! ;; If item is already filed under its category...
! (if (eq major-mode 'todo-filed-mode)
! (save-excursion
! (save-restriction
! (widen)
! ;; ...first find the category name
! (let ((fcat (save-excursion
! (re-search-backward
! (concat "^" (regexp-quote todo-prefix)
! (regexp-quote todo-category-beg)
! "\\(.+\\)" "$"))
! (match-string 1)))
! ;; ...then update the filed item's timestamp but
! ;; don't added a (new) comment before filing away.
! (fitem (buffer-substring
! (progn
! (goto-char (todo-item-start))
! (re-search-forward
! (concat (regexp-quote todo-prefix)
! "[^\\[]*\\["))
! (point))
! (progn
! (goto-char (todo-item-end))
! (beginning-of-line)
! ;; last filed item should not grab
! ;; todo-category-end
! (if (looking-at todo-category-end)
! (1- (point))
! (todo-item-end))))))
! (write-region (concat (time-stamp-string)
! " [" fcat ": " fitem "\n")
! 'end todo-file-done t -1))))
! ;; If item is unfiled, solicit comment before filing.
! (let ((comment (read-from-minibuffer "Comment: ")))
! (write-region (concat (time-stamp-string) " [" cat
! ": " old-timestamp "]" bare-item
! " [Comment: " comment "]\n")
! 'end todo-file-done t -1)))
! (todo-remove-item)
! (message "Item filed in %s." todo-file-done))
! (message ""))
! (error "Not a TODO list entry")))
!
!
! (defun todo-file-item-under-cat ()
! "File the current TODO list entry under its category, comment optional.
!
! The filed items are kept in a separate region of the category, and can be
! displayed by typing \\[todo-display-filed-items-under-cat]."
! (interactive)
! (or (> (count-lines (point-min) (point-max)) 0)
! (error "No TODO list entry to file away"))
! (if (todo-item-string-p)
! (if (y-or-n-p "File this item under its category? ")
! (let ((time-stamp-format todo-time-string-format)
! (cat (nth todo-category-number todo-categories))
! (bare-item (buffer-substring
! (progn
! (goto-char (todo-item-start))
! (search-forward todo-initials))
! (todo-item-end)))
! (old-timestamp (buffer-substring
! (1+ (progn (goto-char (todo-item-start))
! (search-forward todo-prefix)))
! (search-forward todo-initials)))
! (comment (read-from-minibuffer "Comment: "))
! (fill-prefix " ")
! (buffer-read-only nil))
! (save-excursion
! (save-restriction
! (widen)
! ;; make sure category is set up to file items
! (let ((end (save-excursion
! (re-search-forward
! (concat "^" todo-category-end "$"))
! (match-beginning 0))))
! (if (re-search-forward
! (concat "^" todo-category-filed "$") end t)
! (newline)
! (goto-char end)
! (insert todo-category-filed "\n")))
! (insert todo-prefix " " (time-stamp-string)
! " [" old-timestamp "]" bare-item)
! (if (and comment (> (length comment) 0))
! (insert " [Comment: " comment "]"))
! (if (looking-at todo-category-end) (insert "\n"))
! (fill-region (todo-item-start) (point))))
! (todo-remove-item)
! (todo-category-select)
! (message "Item filed. Press `d' to view filed items in this category."))
! (message ""))
! (error "Not a TODO list entry")))
!
! (defun todo-display-filed-items-under-cat ()
! "Display a buffer of the filed TODO items in the current category.
!
! The filed items are displayed in a window below the unfiled items
! of that category. This display uses a special major mode, which contains
! a proper subset of the todo-mode key bindings."
! (interactive)
! (goto-char (point-min))
! (widen)
! (let* ((cat (nth todo-category-number todo-categories))
! (end (save-excursion
! (re-search-forward (concat "^" todo-category-end "$"))
! (match-beginning 0)))
! (begin (re-search-forward
! (concat "^" todo-category-filed "$") end t))
! (name (generate-new-buffer-name todo-filed-buffer))
! (buffer (make-indirect-buffer
! (file-name-nondirectory todo-file-do) name)))
! (if (or (null begin) (equal (1+ begin) end))
(progn
! (todo-category-select)
! (delete-other-windows)
! (error "No filed items in this category"))
! (todo-category-select)
! (set-buffer buffer)
! (todo-filed-mode)
! (narrow-to-region (1+ begin) end)
! (setq mode-line-buffer-identification
! (concat todo-filed-buffer (format "%18s" cat)))
! (display-buffer buffer)
! (message "Type C-x o C-h m to list the functions available in todo-filed-mode."))))
! ; (message "Type C-x 1 to remove filed items or M-C-v to scroll them."))))
!
! (defun todo-move-item ()
! "Move the current todo item to another category."
! (interactive)
! (if (todo-item-string-p)
! (let* ((item (todo-item-string))
! (categories todo-categories)
! (history (cons 'categories (1+ todo-category-number)))
! (category (completing-read (concat "Move to category: ")
! (todo-category-alist) nil nil nil
! history))
! (buffer-read-only nil))
! (if (todo-last-in-category-p)
! (progn
! (todo-remove-item)
! ;; don't leave item separator dangling at the bottom
! (if (search-backward todo-item-separator (point-min) t)
! (progn
! (delete-region (point) (point-max))
! (newline)
! (todo-backward-item))))
! (todo-remove-item))
! ;; currently can't create a category by moving into it (fails
! ;; when todo-add-item-non-interactively is called, but the
! ;; source of the problem currently escapes me)
! (if (member category todo-categories)
! (todo-jump-to-category-noninteractively category)
! (save-excursion (insert item todo-item-separator))
! (error "Cannot move item to a nonexisting category"))
! (todo-add-item-non-interactively item category)
! (message ""))
! (error "Cursor must be on a TODO entry to move it")))
!
! ;; This function was inspired by a feature request from Daniel Ortmann
! ;; on gnu.emacs.bug [srb]
! (defun todo-change-item-sep-in-cat (&optional new)
! "Change the todo item separator in the current category."
! (interactive)
! (save-excursion
! ;; the current separator string in this category (assuming the separator
! ;; string does not contain any alphanumeric character and the last line
! ;; of a todo item does contain at least one alphanumeric character)
! (let ((sep (save-excursion
! (goto-char (point-min))
! (if (re-search-forward
! (concat "\\(\n[^A-Ba-z0-9]*\\)"
! (regexp-quote todo-prefix)) nil t)
! (match-string 1)
! "\n"))))
! (unless (string-equal todo-item-separator sep)
! (setq todo-item-separator sep)))
! (save-excursion
! (goto-char (point-min))
! (when (interactive-p)
! (unless (search-forward todo-prefix nil t 2)
! (error "There must be at least two items to change the separator"))))
! ;; the new separator string
! (unless new
! (setq new (read-from-minibuffer
! (concat "Current item separator string is "
! todo-item-separator ". Replace with: "))))
! (let ((buffer-read-only nil))
! (unless (string-match "\\(^\n$\\|^\n.*\n$\\)" new)
! (error "Separator string must begin and end with a newline"))
! (goto-char (point-min))
! (while (not (todo-last-in-category-p)) ; no separator below last item
! (goto-char (todo-item-end))
! (if (re-search-forward todo-item-separator)
! (replace-match new)
! (re-search-forward (concat "\\(\n.*\\)"
! (regexp-quote todo-prefix)))
! (goto-char (match-beginning 0))
! (replace-match new nil nil nil 1)
! (forward-char)))
! (setq todo-item-separator new))
! (message "New separator string: %s" todo-item-separator)))
!
! (defun todo-change-todo-prefix ()
! "Change the TODO prefix to a new string.
!
! Afterwards, a customization buffer for Todo Prefix is visited, so you
! can save the change for future sessions. If you quit Emacs without doing
! this, problems will result in the next TODO session."
! (interactive)
! (let* ((new (read-from-minibuffer
! (concat "Current item prefix string is "
! todo-prefix ". Replace with: ")))
! (buffer-read-only nil))
! (save-excursion
! (save-restriction
! (widen)
! (goto-char (point-min))
! (while (re-search-forward todo-prefix nil t)
! (replace-match new nil nil))))
! (setq todo-prefix new)
! (todo-category-select))
! (customize-apropos "todo-prefix")
! (search-forward "Todo Prefix")
! (goto-char (match-beginning 0))
! (message "Save for future sessions to make available in next TODO session."))
!
! ;; This function was inspired by a question posted by Kai Großjohann
! ;; on gnu.emacs.help [srb]
! (defun todo-toggle-item-header (arg)
! "Toggle display of TODO item timestamp and initials.
! With prefix argument toggle display of TODO item prefix."
! (interactive "P")
! (goto-char (point-min))
! (while (not (eobp))
! (let ((begin (if arg
! (progn (or (search-forward todo-prefix nil t)
! (error "Done"))
! (match-beginning 0))
! (or (re-search-forward
! (concat (regexp-quote todo-prefix)
! "\\(.*[0-9]+\\) "
! (regexp-quote todo-initials)
! "\\(]?: \\)") nil t)
! (error "Done"))
! (match-beginning 1)))
! (end (if arg
! (1+ (match-end 0))
! (1- (match-end 2))))
! (buffer-read-only nil))
! (or (unless (not (next-single-property-change (point) '(invisible t)
! nil end))
! (add-text-properties begin end '(invisible t)))
! (remove-text-properties begin end '(invisible t))))
(goto-char (todo-item-end))
! (save-buffer 0)))
;; ---------------------------------------------------------------------------
***************
*** 827,832 ****
--- 1446,1486 ----
(todo-add-category category)))
(todo-show)))
+ ;; called by todo-category-click and todo-category-return, and also
+ ;; needed (to avoid mysterious errors I currently don't understand) by
+ ;; todo-add-item-non-interactively and todo-move-item
+ (defun todo-jump-to-category-noninteractively (cat)
+ (let ((name (concat "*" todo-categories-buffer "*")))
+ (if (string= (buffer-name) name)
+ (kill-buffer name)))
+ (switch-to-buffer (file-name-nondirectory todo-file-do))
+ (delete-other-windows)
+ (widen)
+ (goto-char (point-min))
+ (setq todo-category-number (- (length todo-categories)
+ (length (member cat todo-categories))))
+ (todo-category-select))
+
+ (defun todo-category-click (event)
+ (interactive "e")
+ (let (cat)
+ (save-excursion
+ (set-buffer (window-buffer (posn-window (event-end event))))
+ (save-excursion
+ (goto-char (posn-point (event-end event)))
+ (setq cat (buffer-substring (line-beginning-position)
+ (line-end-position))))
+ (select-window (posn-window (event-end event)))
+ (todo-jump-to-category-noninteractively cat))))
+
+ (defun todo-category-return ()
+ (interactive)
+ (let ((cat (buffer-substring (line-beginning-position) (line-end-position))))
+ (save-excursion ; alternatively:
+ (beginning-of-line) ; (require 'thingatpt)
+ (if (looking-at cat) ; (if (thing-at-point-looking-at cat)
+ (todo-jump-to-category-noninteractively cat)))))
+
(defun todo-line-string ()
"Return current line in buffer as a string."
(buffer-substring (line-beginning-position) (line-end-position)))
***************
*** 852,864 ****
"Return point at end of current TODO list item."
(save-excursion
(end-of-line)
! (search-forward-regexp
! (concat "^" (regexp-quote todo-prefix)) nil 'goto-end)
! (1- (line-beginning-position))))
(defun todo-remove-item ()
"Delete the current entry from the TODO list."
! (delete-region (todo-item-start) (1+ (todo-item-end))))
(defun todo-item-string ()
"Return current TODO list entry as a string."
--- 1506,1526 ----
"Return point at end of current TODO list item."
(save-excursion
(end-of-line)
! (or (if (re-search-forward
! (concat (regexp-quote todo-item-separator)
! ; "^" ; see Elisp manual, Regexp Special
! (regexp-quote todo-prefix)) nil t)
! (goto-char (match-beginning 0)))
! (goto-char (1- (point-max)))))) ; no separator after last item
(defun todo-remove-item ()
"Delete the current entry from the TODO list."
! (if (string-equal todo-item-separator "\n")
! (delete-region (todo-item-start) (1+ (todo-item-end)))
! (if (todo-last-in-category-p)
! (delete-region (todo-item-start) (point-max))
! (delete-region (todo-item-start)
! (re-search-forward (regexp-quote todo-item-separator))))))
(defun todo-item-string ()
"Return current TODO list entry as a string."
***************
*** 876,909 ****
"Generate an alist for use in `completing-read' from `todo-categories'."
(mapcar #'list todo-categories))
;; ---------------------------------------------------------------------------
(easy-menu-define todo-menu todo-mode-map "Todo Menu"
'("Todo"
! ["Next category" todo-forward-category t]
! ["Previous category" todo-backward-category t]
! ["Jump to category" todo-jump-to-category t]
! ["Show top priority items" todo-top-priorities t]
! ["Print categories" todo-print t]
"---"
! ["Edit item" todo-edit-item t]
! ["File item" todo-file-item t]
! ["Insert new item" todo-insert-item t]
! ["Insert item here" todo-insert-item-here t]
! ["Kill item" todo-delete-item t]
"---"
! ["Lower item priority" todo-lower-item t]
! ["Raise item priority" todo-raise-item t]
"---"
! ["Next item" todo-forward-item t]
! ["Previous item" todo-backward-item t]
"---"
["Save" todo-save t]
["Save Top Priorities" todo-save-top-priorities t]
"---"
["Quit" todo-quit t]
))
;; As calendar reads .todo-do before todo-mode is loaded.
;;;###autoload
(defun todo-mode ()
--- 1538,1638 ----
"Generate an alist for use in `completing-read' from `todo-categories'."
(mapcar #'list todo-categories))
+ (defun todo-item-separator-p ()
+ "Return non-nil if point is within a TODO item separator string."
+ (unless (string-equal todo-item-separator "\n")
+ (string-match todo-item-separator (todo-item-string) 0)))
+
+ (defun todo-item-string-p ()
+ "Return non-nil if point is on a TODO item."
+ (unless (or (todo-item-separator-p) (eobp))
+ (or (looking-at (regexp-quote todo-prefix))
+ (save-excursion
+ (goto-char (todo-item-end))
+ (looking-at (regexp-quote todo-item-separator)))
+ (todo-last-in-category-p))))
+
+ (defun todo-first-in-category-p ()
+ "Return non-nil if point is on the current first item in the category."
+ (and (todo-item-string-p)
+ (save-excursion
+ (end-of-line)
+ (and (re-search-backward (regexp-quote todo-prefix) nil t)
+ (not (re-search-backward (regexp-quote todo-prefix) nil t))))))
+
+ (defun todo-last-in-category-p ()
+ "Return non-nil if point is on the current last item in the category."
+ (unless (eobp)
+ (save-excursion
+ (end-of-line)
+ (re-search-backward (regexp-quote todo-prefix) nil t)
+ (end-of-line)
+ (not (re-search-forward (regexp-quote todo-prefix) nil t)))))
+
+ (defun todo-count-items ()
+ "Return number of unfiled items in the current category."
+ (goto-char (point-min))
+ (let ((count 0))
+ (save-excursion
+ (while (not (eobp))
+ (re-search-forward (concat "^" todo-prefix))
+ (setq count (1+ count))
+ (goto-char (1+ (todo-item-end))))
+ count)))
+
;; ---------------------------------------------------------------------------
(easy-menu-define todo-menu todo-mode-map "Todo Menu"
'("Todo"
! ["Show Categories" todo-display-categories t]
! ["Next Category" todo-forward-category t]
! ["Previous Category" todo-backward-category t]
! ["Jump to Category" todo-jump-to-category t]
! ["Rename Category" todo-rename-category t]
! ["Add New Category" todo-add-category t]
! ["Delete Category" todo-delete-category t]
! ["Show Top Priority Items" todo-top-priorities t]
! ["Print Categories" todo-print t]
"---"
! ["Edit Item" todo-edit-item t]
! ["File Item" todo-file-item t]
! ["File Item under Category" todo-file-item-under-cat t]
! ["Display Filed Items in Category" todo-display-filed-items-under-cat t]
! ["Insert New Item" todo-insert-item t]
! ["Insert New Item Here" todo-insert-item-here t]
! ["Move Item to Category" todo-move-item t]
! ["Change Item Separator" todo-change-item-sep-in-cat t]
! ["Change Item Prefix" todo-change-todo-prefix t]
! ["Toggle Item Header" todo-toggle-item-header t]
! ["Kill Item" todo-delete-item t]
"---"
! ["Lower Item Priority" todo-lower-item t]
! ["Raise Item Priority" todo-raise-item t]
"---"
! ["Next Item" todo-forward-item t]
! ["Previous Item" todo-backward-item t]
"---"
["Save" todo-save t]
["Save Top Priorities" todo-save-top-priorities t]
"---"
+ ["Customize" (customize-group "todo") t]
+ "---"
["Quit" todo-quit t]
))
+ (easy-menu-define todo-filed-menu todo-filed-mode-map "Todo Filed Menu"
+ '("Todo"
+ ["Edit Item" todo-edit-item t]
+ ["File Item" todo-file-item t]
+ ["Change Item Separator" todo-change-item-sep-in-cat t]
+ ["Toggle Item Header" todo-toggle-item-header t]
+ ["Kill Item" todo-delete-item t]
+ ["Next Item" todo-forward-item t]
+ ["Previous Item" todo-backward-item t]
+ ["Save" todo-save t]
+ ["Quit" todo-kill-buffer t]
+ ))
+
;; As calendar reads .todo-do before todo-mode is loaded.
;;;###autoload
(defun todo-mode ()
***************
*** 911,922 ****
--- 1640,1679 ----
\\{todo-mode-map}"
(interactive)
+ (kill-all-local-variables)
(setq major-mode 'todo-mode)
(setq mode-name "TODO")
+ (make-local-variable 'next-line-add-newlines)
+ (setq next-line-add-newlines t)
+ (make-local-variable 'buffer-read-only)
+ (setq buffer-read-only todo-buffer-read-only)
(use-local-map todo-mode-map)
(easy-menu-add todo-menu)
+ (todo-advice)
(run-hooks 'todo-mode-hook))
+ (defvar todo-disabled-functions '(todo-add-category todo-backward-category todo-delete-category todo-display-filed-items-under-cat todo-display-categories todo-file-item-under-cat todo-forward-category todo-insert-item todo-insert-item-here todo-jump-to-category todo-lower-item todo-move-item todo-raise-item todo-rename-category)
+ "List of TODO functions to be disabled outside of todo-mode.")
+
+ ;; As of Emacs 21.1 `dolist' is defined in subr.el instead of cl.el
+ (if (< emacs-major-version 21) (eval-when-compile (require 'cl)))
+
+ ;; Thanks to Barry Margolin <barmar@genuity.net> and Kevin Rodgers
+ ;; <kevinr@ihs.com>, respectively, for help with the formulation of
+ ;; this function and the advice it contains. [srb]
+ ;;;###autoload
+ (defun todo-advice ()
+ "Disable specified functions when called outside todo-mode."
+ (dolist (fn todo-disabled-functions)
+ (eval `(defadvice ,fn (around todo-disable first (&rest args) activate)
+ (interactive)
+ (cond ((not (eq major-mode 'todo-mode))
+ (error "Disabled in %s mode" mode-name))
+ ((interactive-p)
+ (let ((prefix-arg current-prefix-arg))
+ (call-interactively (ad-make-origname (quote ,fn)))))
+ (t ad-do-it))))))
+
(eval-when-compile
(defvar date)
(defvar entry))
***************
*** 931,938 ****
(define-derived-mode todo-edit-mode text-mode "TODO Edit"
"Major mode for editing items in the TODO list.
! \\{todo-edit-mode-map}")
;;;###autoload
(defun todo-show ()
"Show TODO list."
--- 1688,1734 ----
(define-derived-mode todo-edit-mode text-mode "TODO Edit"
"Major mode for editing items in the TODO list.
! \\{todo-edit-mode-map}"
! (local-set-key (kbd "q") 'todo-kill-buffer)
! (add-hook 'todo-edit-mode-hook 'turn-on-auto-fill))
!
! (defun todo-categories-mode ()
! "Major mode for displaying TODO categories and choosing one to visit.
+ \\{todo-categories-mode-map}"
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map todo-categories-mode-map)
+ (setq major-mode 'todo-categories-mode)
+ (setq mode-name "TODO categories")
+ (setq buffer-read-only todo-buffer-read-only))
+
+ (defun todo-filed-mode ()
+ "Major mode for handling filed TODO items.
+
+ \\{todo-filed-mode-map}"
+ (kill-all-local-variables)
+ (setq major-mode 'todo-filed-mode)
+ (setq mode-name "TODO Filed")
+ (setq buffer-read-only todo-buffer-read-only)
+ (use-local-map todo-filed-mode-map)
+ (easy-menu-add todo-filed-menu))
+
+ (defun todo-kill-buffer (&rest ignore)
+ "Kill the current buffer and return to the TODO item list.
+ Used by `todo-categories-mode', `todo-edit-mode', and `todo-filed-mode'.
+ IGNORE arguments."
+ (interactive)
+ (if (or (eq major-mode 'todo-categories-mode)
+ (eq major-mode 'todo-edit-mode)
+ (eq major-mode 'todo-filed-mode))
+ (progn
+ (kill-buffer (current-buffer))
+ (switch-to-buffer (file-name-nondirectory todo-file-do))
+ (delete-other-windows)
+ (message ""))
+ (error "%s cannot be executed in %s" this-command major-mode)))
+
;;;###autoload
(defun todo-show ()
"Show TODO list."
***************
*** 956,963 ****
"Set up things to work properly in TODO mode."
(find-file todo-file-do)
(erase-buffer)
! (todo-mode)
! (todo-add-category "Todo"))
(provide 'todo-mode)
--- 1752,1764 ----
"Set up things to work properly in TODO mode."
(find-file todo-file-do)
(erase-buffer)
! (let ((first (read-from-minibuffer "Enter a new category: ")))
! (todo-add-category first))
! (write-file todo-file-do)
! (todo-mode))
!
! ;; make sure user's todo-mode customizations are used
! (unless (null custom-file) (load-file custom-file))
(provide 'todo-mode)
^ permalink raw reply [flat|nested] 2+ messages in thread
* quick fix to todo-mode patch + followup question
2002-05-28 23:03 [PATCH] todo-mode extensions and bug fixes Stephen Berman
@ 2002-06-03 10:06 ` Stephen Berman
0 siblings, 0 replies; 2+ messages in thread
From: Stephen Berman @ 2002-06-03 10:06 UTC (permalink / raw)
I just discovered a silly mistake I made in the todo-mode patch I
posted here last Wednesday: I bound `todo-kill-buffer' to `q' in
todo-edit-mode, which prevents `q' from being a self-inserting key
when editing a multiline TODO item. Please apply the below patch to
my previous patch.
I made this diff against my previous patch, which was itself against
the current CVS todo-mode, because it is small and that patch is
rather big. If I should discover further mistakes in my patch code, I
would like to follow this same procedure. Is this appropriate? The
alternative, essentially resubmitting the large patch augmented with a
small change, seems like a waste of bandwidth. This consideration
also leads me to ask the following question: If I implement further
extensions to todo-mode, would it also be acceptable to submit them as
patches to my large patch, i.e., basically to treat my patch as the
status quo of todo-mode? This may seem presumptuous, since my patch
hasn't been applied and may never be. However, the new code in the
patch is almost as long as the todo-mode code contained in Emacs, so
again, diffing against that every time seems like massive redundancy.
A possible alternative would be to rename the file and submit it to
gnu-emacs-sources, but I don't think this is appropriate, since after
all half the code still comes from Oliver Seidel, not me: I haven't
rewritten todo-mode, I've extended it. So to sum up, my general
question is: what's the appropriate way to submit incremental fixes or
extensions to a not-yet-applied large patch to an Emacs package (one
that doubles the code base of the package)?
--Steve Berman
Here are the todo-mode ChangeLog entry and diff against my patch:
2002-06-03 Stephen Berman <steve@ims.uni-stuttgart.de>
* todo-mode-srb.el (todo-edit-mode):
Change key binding of todo-kill-buffer from `q' to
`C-c C-c', so that `q' can be self-inserted in the editing buffer.
(todo-edit-multiline): Change message displayed upon entering editing
buffer to reflect corrected key binding of todo-kill-buffer.
*** todo-mode.el~ Mon Jun 3 11:09:49 2002 (patched version 1.50)
--- todo-mode.el Mon Jun 3 11:09:49 2002
***************
*** 861,867 ****
(make-indirect-buffer
(file-name-nondirectory todo-file-do) buffer-name))
(todo-edit-mode)
! (message "To finish editing and return to TODO item list, type q.")
(widen) ; in case the item is filed
(goto-char here)
;; This bit of code insures that, if the last unfiled or filed item
--- 867,873 ----
(make-indirect-buffer
(file-name-nondirectory todo-file-do) buffer-name))
(todo-edit-mode)
! (message "To finish editing and return to TODO item list, type C-c C-c.")
(widen) ; in case the item is filed
(goto-char here)
;; This bit of code insures that, if the last unfiled or filed item
***************
*** 1869,1875 ****
"Major mode for editing items in the TODO list.
\\{todo-edit-mode-map}"
! (local-set-key (kbd "q") 'todo-kill-buffer)
(add-hook 'todo-edit-mode-hook 'turn-on-auto-fill))
(defun todo-categories-mode ()
--- 1875,1881 ----
"Major mode for editing items in the TODO list.
\\{todo-edit-mode-map}"
! (local-set-key (kbd "C-c C-c") 'todo-kill-buffer)
(add-hook 'todo-edit-mode-hook 'turn-on-auto-fill))
(defun todo-categories-mode ()
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2002-06-03 10:06 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-05-28 23:03 [PATCH] todo-mode extensions and bug fixes Stephen Berman
2002-06-03 10:06 ` quick fix to todo-mode patch + followup question Stephen Berman
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).