Commit | Line | Data |
---|---|---|
7c896f63 | 1 | ;;; todo-mode.el -- Major mode for editing TODO list files |
3cb152f9 | 2 | |
7c896f63 | 3 | ;; Copyright (C) 1997 Free Software Foundation, Inc. |
3cb152f9 | 4 | |
7c896f63 OS |
5 | ;; Author: Oliver.Seidel@cl.cam.ac.uk (was valid on Aug 2, 1997) |
6 | ;; Created: 2 Aug 1997 | |
7f6241ea | 7 | ;; Version: $Id: todo-mode.el,v 1.13 1997/08/19 14:00:36 seidel Exp os10000 $ |
7c896f63 | 8 | ;; Keywords: Categorised TODO list editor, todo-mode |
3cb152f9 | 9 | |
7c896f63 | 10 | ;; This file is part of GNU Emacs. |
595b2334 | 11 | |
7c896f63 | 12 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
595b2334 OS |
13 | ;; it under the terms of the GNU General Public License as published by |
14 | ;; the Free Software Foundation; either version 2, or (at your option) | |
15 | ;; any later version. | |
7c896f63 OS |
16 | |
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
595b2334 OS |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
7c896f63 | 21 | |
595b2334 OS |
22 | ;; You should have received a copy of the GNU General Public License |
23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
24 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
25 | ;; Boston, MA 02111-1307, USA. | |
26 | ||
27 | ;; --------------------------------------------------------------------------- | |
28 | ||
7c896f63 OS |
29 | ;;; Commentary: |
30 | ||
579e1c67 OS |
31 | ;; Quickstart Installation: |
32 | ;; ======================== | |
595b2334 | 33 | ;; |
579e1c67 | 34 | ;; To get this to work, make emacs execute the line |
3d8105fb | 35 | ;; |
7c896f63 | 36 | ;; (require 'todo-mode) ;; load the TODO package |
a360be79 | 37 | ;; |
7f6241ea | 38 | ;; You may now enter new items by typing "M-x todo-insert-item", or enter |
7c896f63 OS |
39 | ;; your the TODO list file by typing "M-x todo-show". |
40 | ;; | |
41 | ;; The TODO list file has a special format and some auxiliary information, | |
42 | ;; which will be added by the todo-show function if it attempts to visit | |
43 | ;; an un-initialised file. Hence it is recommended to use the todo-show | |
44 | ;; function for the first time, in order to initialise the file, but it | |
45 | ;; is not necessary afterwards. | |
46 | ;; | |
47 | ;; As these commands are quite long to type, I would recommend the addition | |
48 | ;; of two bindings to your to your global keymap. I personally have the | |
49 | ;; following in my initialisation file: | |
da2ee685 | 50 | ;; |
579e1c67 | 51 | ;; (global-set-key "\C-ct" 'todo-show) ;; switch to TODO buffer |
7f6241ea | 52 | ;; (global-set-key "\C-ci" 'todo-insert-item) ;; insert new item |
8cdc3b3d | 53 | ;; |
7c896f63 OS |
54 | ;; Note, however, that this recommendation has prompted some criticism, |
55 | ;; since the keys C-c LETTER are reserved for user functions. I believe | |
56 | ;; my recommendation is acceptable, since the Emacs Lisp Manual *Tips* | |
57 | ;; section also details that the mode itself should not bind any functions | |
58 | ;; to those keys. The express aim of the above two bindings is to work | |
59 | ;; outside the mode, which doesn't need the show function and offers | |
60 | ;; a different binding for the insert function. They serve as shortcuts | |
61 | ;; and are not even needed (since the TODO mode will be entered by | |
62 | ;; visiting the TODO file, and later by switching to its buffer). | |
63 | ;; | |
64 | ;; | |
65 | ;; | |
66 | ;; Pre-Requisites | |
67 | ;; ============== | |
68 | ;; | |
69 | ;; This package will require the following packages to be available on | |
70 | ;; the load-path: | |
71 | ;; - time-stamp | |
72 | ;; - easymenu | |
73 | ;; | |
7e6ed9b9 | 74 | ;; |
3cb152f9 | 75 | ;; |
7e6ed9b9 | 76 | ;; Description: |
579e1c67 | 77 | ;; ============ |
7e6ed9b9 | 78 | ;; |
579e1c67 OS |
79 | ;; TODO is a major mode for EMACS which offers functionality to treat |
80 | ;; most lines in one buffer as a list of items one has to do. There | |
81 | ;; are facilities to add new items, which are categorised, to edit or | |
82 | ;; even delete items from the buffer. The buffer contents are currently | |
83 | ;; compatible with the diary, so that the list of todo-items will show | |
84 | ;; up in the FANCY diary mode. | |
7e6ed9b9 | 85 | ;; |
579e1c67 OS |
86 | ;; Notice: Besides the major mode, this file also exports the function |
87 | ;; "todo-show" which will change to the one specific TODO file that has | |
88 | ;; been specified in the todo-file-do variable. If this file does not | |
89 | ;; conform to the TODO mode conventions, the todo-show function will add | |
90 | ;; the appropriate header and footer. I don't anticipate this to cause | |
91 | ;; much grief, but be warned, in case you attempt to read a plain text file. | |
92 | ;; | |
93 | ;; | |
94 | ;; | |
95 | ;; Operation: | |
96 | ;; ========== | |
a360be79 OS |
97 | ;; |
98 | ;; You will have the following facilities available: | |
7e6ed9b9 | 99 | ;; |
03944ca7 OS |
100 | ;; M-x todo-show will enter the todo list screen, here type |
101 | ;; | |
da2ee685 OS |
102 | ;; + to go to next category |
103 | ;; - to go to previous category | |
7e6ed9b9 | 104 | ;; e to edit the current entry |
da2ee685 OS |
105 | ;; f to file the current entry, including a |
106 | ;; comment and timestamp | |
7e6ed9b9 OS |
107 | ;; i to insert a new entry |
108 | ;; k to kill the current entry | |
cf1ebf43 | 109 | ;; l to lower the current entry's priority |
da2ee685 OS |
110 | ;; n for the next entry |
111 | ;; p for the previous entry | |
112 | ;; q to save the list and exit the buffer | |
cf1ebf43 | 113 | ;; r to raise the current entry's priority |
da2ee685 | 114 | ;; s to save the list |
7e6ed9b9 | 115 | ;; |
a360be79 OS |
116 | ;; When you add a new entry, you are asked for the text and then for the |
117 | ;; category. I for example have categories for things that I want to do | |
118 | ;; in the office (like mail my mum), that I want to do in town (like buy | |
119 | ;; cornflakes) and things I want to do at home (move my suitcases). The | |
120 | ;; categories can be selected with the cursor keys and if you type in the | |
121 | ;; name of a category which didn't exist before, an empty category of the | |
03944ca7 | 122 | ;; desired name will be added and filled with the new entry. |
a360be79 | 123 | ;; |
7e6ed9b9 | 124 | ;; |
7e6ed9b9 | 125 | ;; |
579e1c67 OS |
126 | ;; Configuration: |
127 | ;; ============== | |
7e6ed9b9 | 128 | ;; |
579e1c67 OS |
129 | ;; --- todo-prefix |
130 | ;; | |
131 | ;; I would like to recommend that you use the prefix "*/*" (by | |
132 | ;; leaving the variable 'todo-prefix' untouched) so that the diary | |
133 | ;; displays each entry every day. | |
a360be79 | 134 | ;; |
03944ca7 OS |
135 | ;; To understand what I mean, please read the documentation that goes |
136 | ;; with the calendar since that will tell you how you can set up the | |
137 | ;; fancy diary display and use the #include command to include your | |
138 | ;; todo list file as part of your diary. | |
a360be79 | 139 | ;; |
7f6241ea OS |
140 | ;; If you have the diary package set up to usually display more than |
141 | ;; one day's entries at once, consider using | |
142 | ;; "&%%(equal (calendar-current-date) date)" | |
143 | ;; as the value of `todo-prefix'. Please note that this may slow down | |
144 | ;; the processing of your diary file some. | |
145 | ;; | |
579e1c67 OS |
146 | ;; |
147 | ;; --- todo-file-do | |
148 | ;; | |
149 | ;; This variable is fairly self-explanatory. You have to store your TODO | |
150 | ;; list somewhere. This variable tells the package where to go and find | |
151 | ;; this file. | |
152 | ;; | |
153 | ;; | |
154 | ;; --- todo-file-done | |
155 | ;; | |
156 | ;; Even when you're done, you may wish to retain the entries. Given | |
157 | ;; that they're timestamped and you are offered to add a comment, this | |
158 | ;; can make a useful diary of past events. It will even blend in with | |
159 | ;; the EMACS diary package. So anyway, this variable holds the name | |
160 | ;; of the file for the filed todo-items. | |
161 | ;; | |
162 | ;; | |
163 | ;; --- todo-mode-hook | |
164 | ;; | |
165 | ;; Just like other modes, too, this mode offers to call your functions | |
166 | ;; before it goes about its business. This variable will be inspected | |
167 | ;; for any functions you may wish to have called once the other TODO | |
168 | ;; mode preparations have been completed. | |
169 | ;; | |
170 | ;; | |
7f6241ea | 171 | ;; --- todo-insert-treshold |
579e1c67 | 172 | ;; |
3d8105fb OS |
173 | ;; Another nifty feature is the insertion accuracy. If you have 8 items |
174 | ;; in your TODO list, then you may get asked 4 questions by the binary | |
175 | ;; insertion algorithm. However, you may not really have a need for such | |
176 | ;; accurate priorities amongst your TODO items. If you now think about | |
177 | ;; the binary insertion halfing the size of the window each time, then | |
178 | ;; the threshhold is the window size at which it will stop. If you set | |
179 | ;; the threshhold to zero, the upper and lower bound will coincide at the | |
180 | ;; end of the loop and you will insert your item just before that point. | |
181 | ;; If you set the threshhold to i.e. 8, it will stop as soon as the window | |
182 | ;; size drops below that amount and will insert the item in the approximate | |
183 | ;; centre of that window. I got the idea for this feature after reading | |
7c896f63 | 184 | ;; a very helpful e-mail reply from Trey Jackson <trey@cs.berkeley.edu> |
3d8105fb OS |
185 | ;; who corrected some of my awful coding and pointed me towards some good |
186 | ;; reading. Thanks Trey! | |
187 | ;; | |
579e1c67 OS |
188 | ;; |
189 | ;; | |
7c896f63 OS |
190 | ;; |
191 | ;; Things to do: | |
192 | ;; ============= | |
193 | ;; | |
194 | ;; - licence / version function | |
195 | ;; - export to diary file | |
196 | ;; - todo-report-bug | |
197 | ;; - GNATS support | |
198 | ;; - add idea from Urban Boquist <boquist@cs.chalmers.se>: multi-line-entries | |
199 | ;; - 'e' opens buffer for multi-line entry | |
200 | ;; - elide multiline | |
201 | ;; - rewrite complete package to store data as lisp objects and have | |
202 | ;; display modes for display, for diary export, etc. | |
203 | ;; | |
204 | ;; | |
205 | ;; | |
579e1c67 OS |
206 | ;; History and Gossip: |
207 | ;; =================== | |
208 | ;; | |
cf1ebf43 OS |
209 | ;; Many thanks to all the ones who have contributed to the evolution of this |
210 | ;; package! I hope I have listed all of you somewhere in the documentation | |
211 | ;; or at least in the RCS history! | |
212 | ;; | |
cf1ebf43 OS |
213 | ;; Enjoy this package and express your gratitude by sending nice things |
214 | ;; to my parents' address! | |
a360be79 OS |
215 | ;; |
216 | ;; Oliver Seidel | |
579e1c67 | 217 | ;; |
cf1ebf43 | 218 | ;; (O Seidel, Lessingstr. 8, 65760 Eschborn, Federal Republic of Germany) |
579e1c67 OS |
219 | ;; |
220 | ||
221 | ;; --------------------------------------------------------------------------- | |
222 | ||
7c896f63 OS |
223 | ;; --------------------------------------------------------------------------- |
224 | ||
225 | ;;; Change Log: | |
226 | ||
227 | ;; $Log: todo-mode.el,v $ | |
7f6241ea OS |
228 | ;; Revision 1.13 1997/08/19 14:00:36 seidel |
229 | ;; - changed name to todo-mode | |
230 | ;; - fixed menu descriptions | |
231 | ;; - fixed "pressing abort while filing" | |
232 | ;; - attempted Emacs Lisp Manual *Tips* section compliance | |
233 | ;; | |
7c896f63 OS |
234 | ;; Revision 1.12 1997/08/06 10:56:15 os10000 |
235 | ;; Fixed header, typos, layout, documentation. | |
579e1c67 | 236 | ;; |
cf1ebf43 OS |
237 | ;; Revision 1.11 1997/08/06 09:14:25 os10000 |
238 | ;; Applied patch from Istvan Marko <istvan@cmdmail.amd.com> | |
239 | ;; to make menus work anywhere. | |
240 | ;; | |
49c48a1b OS |
241 | ;; Revision 1.10 1997/08/06 08:56:03 os10000 |
242 | ;; Acted upon suggestion from Shane Holder <holder@rsn.hp.com>: | |
243 | ;; Cancelling the editing of an entry will not delete it any more. | |
244 | ;; | |
03944ca7 OS |
245 | ;; Revision 1.9 1997/08/06 08:12:03 os10000 |
246 | ;; Improved documentation. Broke some lines to comply with | |
247 | ;; Richard Stallman's email to please keep in sync with the | |
248 | ;; rest of the Emacs distribution files. | |
249 | ;; | |
579e1c67 | 250 | ;; Revision 1.8 1997/08/05 22:39:04 os10000 |
7c896f63 | 251 | ;; Made todo-mode.el available under GPL. |
579e1c67 OS |
252 | ;; |
253 | ;; Revision 1.7 1997/08/05 22:34:14 os10000 | |
254 | ;; Fixed insertion routine with help from Trey Jackson | |
7f6241ea | 255 | ;; <trey@cs.berkeley.edu>; added todo-inst-tresh; |
579e1c67 OS |
256 | ;; fixed keyboard layout to remove unwanted keys. |
257 | ;; | |
258 | ;; Revision 1.6 1997/08/05 16:47:01 os10000 | |
259 | ;; Incorporated menus for XEmacs from Allan.Cochrane@soton.sc.philips.com, | |
260 | ;; fixed TYPO, fixed todo-file-cmd, cleaned up rcs history. | |
261 | ;; | |
262 | ;; Revision 1.5 1997/08/05 14:43:39 os10000 | |
263 | ;; Added improvements from Ron Gut <rgut@aware.com>. | |
264 | ;; Added category management. | |
265 | ;; | |
266 | ;; Revision 1.4 1997/08/04 16:18:45 os10000 | |
267 | ;; Added Raise/Lower item. | |
268 | ;; | |
269 | ;; Revision 1.3 1997/08/03 12:47:26 os10000 | |
270 | ;; Cleaned up variables, prefix and cursor position. | |
271 | ;; | |
272 | ;; Revision 1.2 1997/08/03 12:15:28 os10000 | |
273 | ;; It appears to work. | |
274 | ;; | |
275 | ;; Revision 1.1 1997/08/03 12:15:13 os10000 | |
276 | ;; Initial revision | |
277 | ;; | |
7e6ed9b9 OS |
278 | |
279 | ;; --------------------------------------------------------------------------- | |
280 | ||
7c896f63 OS |
281 | ;;; Code: |
282 | ||
3cb152f9 OS |
283 | ;; User-configurable variables: |
284 | ||
cf1ebf43 OS |
285 | (defvar todo-prefix "*/*" "TODO mode prefix for entries.") |
286 | (defvar todo-file-do "~/.todo-do" "TODO mode list file.") | |
287 | (defvar todo-file-done "~/.todo-done" "TODO mode archive file.") | |
288 | (defvar todo-mode-hook nil "TODO mode hooks.") | |
7f6241ea OS |
289 | (defvar todo-edit-mode-hook nil "TODO Edit mode hooks.") |
290 | (defvar todo-insert-treshold 0 "TODO mode insertion accuracy.") | |
291 | (defvar todo-edit-buffer " *TODO Edit*" "TODO Edit buffer name.") | |
7c896f63 OS |
292 | |
293 | ;; Thanks for the ISO time stamp format go to Karl Eichwalder <ke@suse.de> | |
294 | ;; My format string for the appt.el package is "%3b %2d, %y, %02I:%02M%p". | |
295 | ;; | |
296 | (defvar todo-time-string-format "%y-%02m-%02d %02H:%02M" | |
297 | "TODO mode time string format for done entries. | |
298 | For details see the variable `time-stamp-format'.") | |
299 | ||
3cb152f9 OS |
300 | ;; --------------------------------------------------------------------------- |
301 | ||
49c48a1b OS |
302 | ;; Get some outside help ... |
303 | ||
3cb152f9 | 304 | (require 'time-stamp) |
49c48a1b OS |
305 | (require 'easymenu) |
306 | ||
307 | ;; --------------------------------------------------------------------------- | |
3cb152f9 | 308 | |
cf1ebf43 OS |
309 | ;; Set up some helpful context ... |
310 | ||
7f6241ea OS |
311 | (defvar todo-categories nil "TODO categories.") |
312 | (defvar todo-previous-line 0 "previous line that I asked about.") | |
313 | (defvar todo-previous-answer 0 "previous answer that I got.") | |
cf1ebf43 OS |
314 | (defvar todo-mode-map nil "TODO mode keymap.") |
315 | (defvar todo-category-number 0 "TODO category number.") | |
316 | ||
317 | ;; --------------------------------------------------------------------------- | |
318 | ||
3d8105fb OS |
319 | (if todo-mode-map |
320 | nil | |
321 | (let ((map (make-keymap))) | |
322 | (suppress-keymap map t) | |
7f6241ea OS |
323 | (define-key map "+" 'todo-forward-category) |
324 | (define-key map "-" 'todo-backward-category) | |
325 | (define-key map "e" 'todo-edit-item) | |
326 | (define-key map "E" 'todo-edit-multiline) | |
327 | (define-key map "f" 'todo-file-item) | |
328 | (define-key map "i" 'todo-insert-item) | |
329 | (define-key map "k" 'todo-delete-item) | |
330 | (define-key map "l" 'todo-lower-item) | |
331 | (define-key map "n" 'todo-forward-item) | |
332 | (define-key map "p" 'todo-backward-item) | |
333 | (define-key map "q" 'todo-quit) | |
334 | (define-key map "r" 'todo-raise-item) | |
335 | (define-key map "s" 'todo-save) | |
3d8105fb | 336 | (setq todo-mode-map map))) |
3cb152f9 | 337 | |
7f6241ea OS |
338 | (defun todo-category-select () |
339 | "Make TODO mode display the current category correctly." | |
340 | (let ((name (nth todo-category-number todo-categories))) | |
579e1c67 | 341 | (setq mode-line-buffer-identification |
7f6241ea | 342 | (concat "Category: " name)) |
da2ee685 OS |
343 | (widen) |
344 | (goto-char (point-min)) | |
7f6241ea OS |
345 | (search-forward-regexp |
346 | (concat "^" (regexp-quote (concat todo-prefix " --- " name)))) | |
347 | (let ((begin (1+ (point-at-eol)))) | |
348 | (search-forward-regexp "^--- End") | |
349 | (narrow-to-region begin (point-at-bol)) | |
350 | (goto-char (point-min))))) | |
351 | (defalias 'todo-cat-slct 'todo-category-select) | |
352 | ||
353 | (defun todo-forward-category () "Go forward to TODO list of next category." | |
da2ee685 | 354 | (interactive) |
7f6241ea OS |
355 | (setq todo-category-number |
356 | (mod (1+ todo-category-number) (length todo-categories))) | |
357 | (todo-category-select)) | |
358 | (defalias 'todo-cmd-forw 'todo-forward-category) | |
da2ee685 | 359 | |
7f6241ea | 360 | (defun todo-backward-category () "Go back to TODO list of previous category." |
da2ee685 | 361 | (interactive) |
7f6241ea OS |
362 | (setq todo-category-number |
363 | (mod (1- todo-category-number) (length todo-categories))) | |
364 | (todo-category-select)) | |
365 | (defalias 'todo-cmd-back 'todo-backward-category) | |
da2ee685 | 366 | |
7f6241ea | 367 | (defun todo-backward-item () "Select previous entry of TODO list." |
3cb152f9 | 368 | (interactive) |
7f6241ea | 369 | (search-backward-regexp (concat "^" (regexp-quote todo-prefix)) nil t) |
cf1ebf43 | 370 | (message "")) |
7f6241ea | 371 | (defalias 'todo-cmd-prev 'todo-backward-item) |
3cb152f9 | 372 | |
7f6241ea | 373 | (defun todo-forward-item () "Select next entry of TODO list." |
3cb152f9 | 374 | (interactive) |
7f6241ea OS |
375 | (end-of-line) |
376 | (search-forward-regexp (concat "^" (regexp-quote todo-prefix)) nil 'goto-end) | |
377 | (beginning-of-line) | |
cf1ebf43 | 378 | (message "")) |
7f6241ea | 379 | (defalias 'todo-cmd-next 'todo-forward-item) |
3cb152f9 | 380 | |
7f6241ea | 381 | (defun todo-save () "Save the TODO list." |
da2ee685 | 382 | (interactive) |
cf1ebf43 | 383 | (save-buffer)) |
7f6241ea | 384 | (defalias 'todo-cmd-save 'todo-save) |
da2ee685 | 385 | |
7f6241ea | 386 | (defun todo-quit () "Done with TODO list for now." |
3cb152f9 | 387 | (interactive) |
da2ee685 OS |
388 | (widen) |
389 | (save-buffer) | |
7e6ed9b9 | 390 | (message "") |
cf1ebf43 | 391 | (bury-buffer)) |
7f6241ea | 392 | (defalias 'todo-cmd-done 'todo-quit) |
3cb152f9 | 393 | |
7f6241ea | 394 | (defun todo-edit-item () "Edit current TODO list entry." |
3cb152f9 | 395 | (interactive) |
7f6241ea OS |
396 | (let ((item (todo-item-string))) |
397 | (if (todo-string-multiline-p item) | |
398 | (todo-edit-multiline) | |
399 | (let ((new (read-from-minibuffer "Edit: " item))) | |
400 | (todo-remove-item) | |
401 | (insert new "\n") | |
402 | (todo-backward-item) | |
403 | (message ""))))) | |
404 | (defalias 'todo-cmd-edit 'todo-edit-item) | |
405 | ||
406 | (defun todo-edit-multiline () | |
407 | "Set up a buffer for editing a multiline TODO list entry." | |
408 | (interactive) | |
409 | (let ((buffer-name (generate-new-buffer-name todo-edit-buffer))) | |
410 | (pop-to-buffer (make-indirect-buffer (file-name-nondirectory todo-file-do) | |
411 | buffer-name)) | |
412 | (todo-edit-mode) | |
413 | (narrow-to-region (todo-item-start) (todo-item-end)))) | |
3cb152f9 | 414 | |
da2ee685 | 415 | (defun todo-add-category (cat) "Add a new category to the TODO list." |
3cb152f9 | 416 | (interactive) |
3cb152f9 | 417 | (save-window-excursion |
7f6241ea | 418 | (setq todo-categories (cons cat todo-categories)) |
3cb152f9 | 419 | (find-file todo-file-do) |
da2ee685 OS |
420 | (widen) |
421 | (goto-char (point-min)) | |
422 | (let ((posn (search-forward "-*- mode: todo; " 17 t))) | |
423 | (if (not (null posn)) (goto-char posn)) | |
579e1c67 OS |
424 | (if (equal posn nil) |
425 | (progn | |
426 | (insert "-*- mode: todo; \n") | |
427 | (forward-char -1)) | |
428 | (kill-line))) | |
7f6241ea | 429 | (insert (format "todo-categories: %S; -*-" todo-categories)) |
da2ee685 | 430 | (forward-char 1) |
579e1c67 | 431 | (insert (format "%s --- %s\n--- End\n%s %s\n" |
cf1ebf43 OS |
432 | todo-prefix cat todo-prefix (make-string 75 ?-)))) |
433 | 0) | |
da2ee685 | 434 | |
7f6241ea | 435 | (defun todo-insert-item () |
3d8105fb | 436 | "Insert new TODO list entry." |
da2ee685 | 437 | (interactive) |
7f6241ea OS |
438 | (let* ((new-item (concat todo-prefix " " |
439 | (read-from-minibuffer "New TODO entry: "))) | |
440 | (categories todo-categories) | |
441 | (history (cons 'categories (1+ todo-category-number))) | |
442 | (category (completing-read "Category: " | |
443 | (todo-category-alist) nil nil | |
444 | (nth todo-category-number todo-categories) | |
445 | history))) | |
446 | (let ((cat-exists (member category todo-categories))) | |
447 | (setq todo-category-number | |
448 | (if cat-exists | |
449 | (- (length todo-categories) (length cat-exists)) | |
450 | (todo-add-category category)))) | |
451 | (todo-show) | |
452 | (setq todo-previous-line 0) | |
453 | (let ((top 1) | |
454 | (bottom (1+ (count-lines (point-min) (point-max))))) | |
455 | (while (> (- bottom top) todo-insert-treshold) | |
456 | (let* ((current (/ (+ top bottom) 2)) | |
457 | (answer (if (< current bottom) | |
458 | (todo-more-important-p current) nil))) | |
459 | (if answer | |
460 | (setq bottom current) | |
461 | (setq top (1+ current))))) | |
462 | (setq top (/ (+ top bottom) 2)) | |
463 | ;; goto-line doesn't have the desired behavior in a narrowed buffer | |
464 | (goto-char (point-min)) | |
465 | (forward-line (1- top))) | |
466 | (insert new-item "\n") | |
467 | (todo-backward-item) | |
7c896f63 | 468 | (save-buffer) |
3d8105fb | 469 | (message ""))) |
7f6241ea | 470 | (defalias 'todo-cmd-inst 'todo-insert-item) |
3d8105fb | 471 | |
7f6241ea OS |
472 | (defun todo-more-important-p (line) |
473 | "Ask whether entry is more important than the one at LINE." | |
474 | (if (not (equal todo-previous-line line)) | |
3d8105fb | 475 | (progn |
7f6241ea | 476 | (setq todo-previous-line line) |
3d8105fb | 477 | (goto-char (point-min)) |
7f6241ea OS |
478 | (forward-line (1- todo-previous-line)) |
479 | (let ((item (todo-item-string-start))) | |
480 | (setq todo-previous-answer | |
481 | (y-or-n-p (concat "More important than '" item "'? ")))))) | |
482 | todo-previous-answer) | |
483 | (defalias 'todo-ask-p 'todo-more-important-p) | |
484 | ||
485 | (defun todo-delete-item () "Delete current TODO list entry." | |
3cb152f9 OS |
486 | (interactive) |
487 | (if (> (count-lines (point-min) (point-max)) 0) | |
7f6241ea OS |
488 | (let* ((todo-entry (todo-item-string-start)) |
489 | (todo-answer (y-or-n-p (concat "Permanently remove '" | |
490 | todo-entry "'? ")))) | |
491 | (if todo-answer | |
492 | (progn | |
493 | (todo-remove-item) | |
494 | (todo-backward-item))) | |
cf1ebf43 | 495 | (message "")) |
7f6241ea OS |
496 | (error "No TODO list entry to delete"))) |
497 | (defalias 'todo-cmd-kill 'todo-delete-item) | |
3cb152f9 | 498 | |
7f6241ea | 499 | (defun todo-raise-item () "Raise priority of current entry." |
8cdc3b3d | 500 | (interactive) |
7f6241ea OS |
501 | (if (> (count-lines (point-min) (point)) 0) |
502 | (let ((item (todo-item-string))) | |
503 | (todo-remove-item) | |
504 | (todo-backward-item) | |
505 | (save-excursion | |
506 | (insert item "\n")) | |
cf1ebf43 | 507 | (message "")) |
7f6241ea OS |
508 | (error "No TODO list entry to raise"))) |
509 | (defalias 'todo-cmd-rais 'todo-raise-item) | |
8cdc3b3d | 510 | |
7f6241ea | 511 | (defun todo-lower-item () "Lower priority of current entry." |
8cdc3b3d | 512 | (interactive) |
7f6241ea OS |
513 | (if (> (count-lines (point) (point-max)) 1) ; Assume there is a final newline |
514 | (let ((item (todo-item-string))) | |
515 | (todo-remove-item) | |
516 | (todo-forward-item) | |
517 | (save-excursion | |
518 | (insert item "\n")) | |
cf1ebf43 | 519 | (message "")) |
7f6241ea OS |
520 | (error "No TODO list entry to lower"))) |
521 | (defalias 'todo-cmd-lowr 'todo-lower-item) | |
8cdc3b3d | 522 | |
7f6241ea | 523 | (defun todo-file-item () "File the current TODO list entry away." |
3cb152f9 OS |
524 | (interactive) |
525 | (if (> (count-lines (point-min) (point-max)) 0) | |
7f6241ea OS |
526 | (let ((comment (read-from-minibuffer "Comment: ")) |
527 | (time-stamp-format todo-time-string-format)) | |
528 | (goto-char (todo-item-start)) | |
529 | (delete-region (point) (search-forward todo-prefix)) | |
530 | (insert (time-stamp-string)) | |
531 | (goto-char (todo-item-end)) | |
532 | (insert (if (save-excursion (beginning-of-line) | |
533 | (looking-at (regexp-quote todo-prefix))) | |
534 | " " | |
535 | "\n\t") | |
536 | "(" (nth todo-category-number todo-categories) ": " | |
537 | comment ")") | |
538 | (append-to-file (todo-item-start) (todo-item-end) todo-file-done) | |
539 | (todo-remove-item) | |
540 | (todo-backward-item) | |
cf1ebf43 | 541 | (message "")) |
7f6241ea | 542 | (error "No TODO list entry to file away"))) |
3cb152f9 OS |
543 | |
544 | ;; --------------------------------------------------------------------------- | |
545 | ||
7f6241ea OS |
546 | ;; Utility functions: |
547 | ||
548 | (defun todo-line-string () "Return current line in buffer as a string." | |
549 | (buffer-substring (point-at-bol) (point-at-eol))) | |
550 | ||
551 | (defun todo-item-string-start () | |
552 | "Return the start of this TODO list entry as a string." | |
553 | ;; Suitable for putting in the minibuffer when asking the user | |
554 | (let ((item (todo-item-string))) | |
555 | (if (> (length item) 60) | |
556 | (setq item (concat (substring item 0 56) "..."))) | |
557 | item)) | |
558 | ||
559 | (defun todo-item-start () "Return point at start of current TODO list item." | |
560 | (save-excursion | |
561 | (beginning-of-line) | |
562 | (if (not (looking-at (regexp-quote todo-prefix))) | |
563 | (search-backward-regexp | |
564 | (concat "^" (regexp-quote todo-prefix)) nil t)) | |
565 | (point))) | |
566 | ||
567 | (defun todo-item-end () "Return point at end of current TODO list item." | |
568 | (save-excursion | |
569 | (end-of-line) | |
570 | (search-forward-regexp (concat "^" (regexp-quote todo-prefix)) nil 'goto-end) | |
571 | (1- (point-at-bol)))) | |
572 | ||
573 | (defun todo-remove-item () "Delete the current entry from the TODO list." | |
574 | (delete-region (todo-item-start) (1+ (todo-item-end)))) | |
575 | ||
576 | (defun todo-item-string () "Return current TODO list entry as a string." | |
577 | (buffer-substring (todo-item-start) (todo-item-end))) | |
578 | ||
579 | (defun todo-string-count-lines (string) | |
580 | "Return the number of lines STRING spans." | |
581 | (length (split-string string "\n"))) | |
582 | ||
583 | (defun todo-string-multiline-p (string) | |
584 | "Returns non-nil if STRING spans several lines" | |
585 | (> (todo-string-count-lines string) 1)) | |
586 | ||
587 | (defun todo-category-alist () | |
588 | "Generate an alist fro use in `completing-read' from `todo-categories'" | |
589 | (let (alist) | |
590 | (mapcar (lambda (cat) (setq alist (cons (cons cat nil) alist))) | |
591 | todo-categories) | |
592 | alist)) | |
593 | ||
da2ee685 OS |
594 | ;; utility functions: These are available in XEmacs, but not in Emacs 19.34 |
595 | ||
596 | (if (not (fboundp 'point-at-bol)) | |
7f6241ea | 597 | (defun point-at-bol () "Return value of point at beginning of line." |
da2ee685 OS |
598 | (save-excursion |
599 | (beginning-of-line) | |
600 | (point)))) | |
601 | ||
602 | (if (not (fboundp 'point-at-eol)) | |
7f6241ea | 603 | (defun point-at-eol () "Return value of point at end of line." |
da2ee685 OS |
604 | (save-excursion |
605 | (end-of-line) | |
606 | (point)))) | |
607 | ||
608 | ;; --------------------------------------------------------------------------- | |
609 | ||
49c48a1b | 610 | (easy-menu-define todo-menu todo-mode-map "Todo Menu" |
7c896f63 | 611 | '("Todo" |
7f6241ea OS |
612 | ["Next category" todo-forward-category t] |
613 | ["Previous category" todo-backward-category t] | |
7c896f63 | 614 | "---" |
7f6241ea OS |
615 | ["Edit item" todo-edit-item t] |
616 | ["File item" todo-file-item t] | |
617 | ["Insert new item" todo-insert-item t] | |
618 | ["Kill item" todo-delete-item t] | |
7c896f63 | 619 | "---" |
7f6241ea OS |
620 | ["Lower item priority" todo-lower-item t] |
621 | ["Raise item priority" todo-raise-item t] | |
7c896f63 | 622 | "---" |
7f6241ea OS |
623 | ["Next item" todo-forward-item t] |
624 | ["Previous item" todo-backward-item t] | |
7c896f63 | 625 | "---" |
7f6241ea | 626 | ["Save" todo-save t] |
7c896f63 | 627 | "---" |
7f6241ea | 628 | ["Quit" todo-quit t] |
7c896f63 | 629 | )) |
a360be79 | 630 | |
da2ee685 | 631 | (defun todo-mode () "Major mode for editing TODO lists.\n\n\\{todo-mode-map}" |
3cb152f9 | 632 | (interactive) |
3cb152f9 OS |
633 | (setq major-mode 'todo-mode) |
634 | (setq mode-name "TODO") | |
635 | (use-local-map todo-mode-map) | |
49c48a1b OS |
636 | (easy-menu-add todo-menu) |
637 | (run-hooks 'todo-mode-hook)) | |
da2ee685 | 638 | |
7f6241ea OS |
639 | (defun todo-edit-mode () |
640 | "Major mode for editing items in the TODO list\n\n\\{todo-edit-mode-map}" | |
641 | (text-mode) | |
642 | (setq major-mode 'todo-edit-mode) | |
643 | (setq mode-name "TODO Edit") | |
644 | (run-hooks 'todo-edit-mode-hook)) | |
645 | ||
da2ee685 OS |
646 | (defun todo-show () "Show TODO list." |
647 | (interactive) | |
7f6241ea OS |
648 | (if (file-exists-p todo-file-do) |
649 | (find-file todo-file-do) | |
650 | (todo-initial-setup)) | |
651 | (if (null todo-categories) | |
652 | (if (null todo-cats) | |
653 | (error "Error in %s: No categories in list `todo-categories'" | |
654 | todo-file-do) | |
655 | (make-local-variable todo-categories) | |
656 | (setq todo-categories todo-cats))) | |
657 | (beginning-of-line) | |
658 | (todo-category-select)) | |
659 | ||
660 | (defun todo-initial-setup () "Set up things to work properly in TODO mode." | |
da2ee685 | 661 | (find-file todo-file-do) |
7f6241ea OS |
662 | (erase-buffer) |
663 | (todo-mode) | |
664 | (todo-add-category "Todo")) | |
3cb152f9 | 665 | |
7c896f63 | 666 | (provide 'todo-mode) |
3cb152f9 OS |
667 | |
668 | ;; --------------------------------------------------------------------------- | |
7c896f63 | 669 | ;;; todo-mode.el ends here |
3cb152f9 | 670 | ;; --------------------------------------------------------------------------- |