diff options
| author | Stephen Berman | 2010-06-18 23:52:10 +0100 |
|---|---|---|
| committer | Stephen Berman | 2010-06-18 23:52:10 +0100 |
| commit | ee7412e467393ef608f1b1f868e368ab923ae406 (patch) | |
| tree | f9fe5f7b3a5ea08051c4010741eba9142c935ac0 | |
| parent | 2c173503dcb83660571ab5abe244cc559eb61596 (diff) | |
| download | emacs-ee7412e467393ef608f1b1f868e368ab923ae406.tar.gz emacs-ee7412e467393ef608f1b1f868e368ab923ae406.zip | |
* calendar/todos.el: Numerous spelling and comment fixes, doc
string fixes to conform with checkdoc, further rearrangement of
definitions, etc.
(todos-previous-line, todos-previous-answer)
(todos-insert-item-ask-date, todos-change-date)
(todos-date-nodayname-pattern, todos-dayname-date-pattern)
(todos-count-items-in-category, todos-count-all-items): Remove.
(todos-update-numbered-prefix, todos-item-start-overlays)
(todos-add-item-non-interactively): Comment out, also in uses.
(todos-done-separator): Change default value.
(todos-always-add-time-string): Rename from todos-add-time-string
and adjust uses.
(todos-read-category): Rename from todos-completing-read and
adjust callers; use todos-current-category.
(todos-make-categories-alist): Rename from function
todos-categories-alist.
(todos-categories-alist): New variable.
(todos-indent-to-here): New defcustom.
(todos-button): New face.
(todos-display-categories-alphabetically, todos-raise-category)
(todos-lower-category, todos-insert-item-for-diary-ask-date-time)
(todos-insert-item-here-ask-date-time)
(todos-insert-item-ask-date-time)
(todos-insert-item-ask-dayname-time): New commands.
(todos-edit-item-header): New command replacing todos-change-date.
(todos-category-number, todos-indent, todos-item-counts)
(todos-check-category-name, todos-read-date, todos-read-dayname)
(todos-read-time, todos-padded-string)
(todos-insert-category-name): New functions.
(todos-set-item-priority): New function replacing
todos-add-item-non-interactively.
(todos-mode-map): Remap newline to newline-and-indent.
(todos-edit-mode-map): Make sparse keymap; remap newline to
newline-and-indent.
(todos-categories-mode-map): New keymap.
(todos-mode, todos-edit-mode): Make indent-line-function local
variable and set to todos-indent.
(todos-categories-mode): New major mode.
(todos-display-categories): List categories initially in their
numerical order; add optional argument to switch to alphabetical
listing.
(todos-toggle-view-done-items): Simplify implementation.
(todos-toggle-display-date-time): Fix regexp search string.
(todos-backward-item, todos-forward-item): Use variable
todos-item-start.
(todos-add-category): Use todos-check-category-name and
todos-categories-alist.
(todos-rename-category): Use todos-current-category,
todos-check-category-name and todos-categories-alist.
(todos-delete-category): Use todos-check-category-name and
todos-categories-alist and take done items into account.
(todos-insert-item): Use separate arguments to handle insertion of
date/dayname and time strings, add new argument to mark item for
diary inclusion, use new todos-read-* functions,
todos-set-item-priority and todos-item-counts.
(todos-insert-item-here, todos-insert-item-for-diary)
(todos-insert-item-from-calendar): Adapt to new version of
todos-insert-item.
(todos-delete-item, todos-item-done): Use todos-item-counts.
(todos-edit-item): Indent newlines inserted by C-q C-j if nonspace
char follows.
(todos-lower-item): Ensure only not-done items can be lowered.
(todos-move-item): Use todos-current-category, todos-read-category
and todos-item-counts.
(todos-archive-done-items): Use todos-current-category and
todos-item-counts; fix regexp search string.
(todos-item-undo): Use todos-current-category,
todos-set-item-priority, todos-insert-with-overlays and
todos-item-counts; restore if user quits before inserting undone
item.
(todos-date-pattern): Rewrite without using
todos-date-nodayname-pattern and todos-dayname-date-pattern.
(todos-date-string-match): Do not make todos-date-pattern an
unnumbered group.
(todos-time-string-match): Make todos-date-pattern a shy group.
(todos-wrap-and-indent): Use todos-indent-to-here.
(todos-reset-prefix): Revert to using todos-show instead of
todos-category-select.
(todos-prefix-overlays): Fix numbering of done items and updating
of prefix.
(todos-category-select): Use todos-current-category; fix display
of separator string; don't move point to top of category.
(todos-jump-to-category-noninteractively): Use todos-category-number.
(todos-insert-with-overlays): Use todos-item-start unconditionally.
(todos-item-start): New variable.
(todos-item-start): Use it to define this function.
(todos-item-end): Adjust if item is last unfinished one before
displayed done items.
(todos-remove-item): Use todos-item-start and todos-item-end
instead of todos-forward-item and todos-backward-item.
(todos-longest-category-name-length): Add argument for list of
categories.
| -rw-r--r-- | lisp/ChangeLog | 100 | ||||
| -rw-r--r-- | lisp/calendar/todos.el | 1761 |
2 files changed, 984 insertions, 877 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e2d88a96ac4..0bcb61c3712 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,5 +1,100 @@ | |||
| 1 | 2012-09-13 Stephen Berman <stephen.berman@gmx.net> | 1 | 2012-09-13 Stephen Berman <stephen.berman@gmx.net> |
| 2 | 2 | ||
| 3 | * calendar/todos.el: Numerous spelling and comment fixes, doc | ||
| 4 | string fixes to conform with checkdoc, further rearrangement of | ||
| 5 | definitions, etc. | ||
| 6 | (todos-previous-line, todos-previous-answer) | ||
| 7 | (todos-insert-item-ask-date, todos-change-date) | ||
| 8 | (todos-date-nodayname-pattern, todos-dayname-date-pattern) | ||
| 9 | (todos-count-items-in-category, todos-count-all-items): Remove. | ||
| 10 | (todos-update-numbered-prefix, todos-item-start-overlays) | ||
| 11 | (todos-add-item-non-interactively): Comment out, also in uses. | ||
| 12 | (todos-done-separator): Change default value. | ||
| 13 | (todos-always-add-time-string): Rename from todos-add-time-string | ||
| 14 | and adjust uses. | ||
| 15 | (todos-read-category): Rename from todos-completing-read and | ||
| 16 | adjust callers; use todos-current-category. | ||
| 17 | (todos-make-categories-alist): Rename from function | ||
| 18 | todos-categories-alist. | ||
| 19 | (todos-categories-alist): New variable. | ||
| 20 | (todos-indent-to-here): New defcustom. | ||
| 21 | (todos-button): New face. | ||
| 22 | (todos-display-categories-alphabetically, todos-raise-category) | ||
| 23 | (todos-lower-category, todos-insert-item-for-diary-ask-date-time) | ||
| 24 | (todos-insert-item-here-ask-date-time) | ||
| 25 | (todos-insert-item-ask-date-time) | ||
| 26 | (todos-insert-item-ask-dayname-time): New commands. | ||
| 27 | (todos-edit-item-header): New command replacing todos-change-date. | ||
| 28 | (todos-category-number, todos-indent, todos-item-counts) | ||
| 29 | (todos-check-category-name, todos-read-date, todos-read-dayname) | ||
| 30 | (todos-read-time, todos-padded-string) | ||
| 31 | (todos-insert-category-name): New functions. | ||
| 32 | (todos-set-item-priority): New function replacing | ||
| 33 | todos-add-item-non-interactively. | ||
| 34 | (todos-mode-map): Remap newline to newline-and-indent. | ||
| 35 | (todos-edit-mode-map): Make sparse keymap; remap newline to | ||
| 36 | newline-and-indent. | ||
| 37 | (todos-categories-mode-map): New keymap. | ||
| 38 | (todos-mode, todos-edit-mode): Make indent-line-function local | ||
| 39 | variable and set to todos-indent. | ||
| 40 | (todos-categories-mode): New major mode. | ||
| 41 | (todos-display-categories): List categories initially in their | ||
| 42 | numerical order; add optional argument to switch to alphabetical | ||
| 43 | listing. | ||
| 44 | (todos-toggle-view-done-items): Simplify implementation. | ||
| 45 | (todos-toggle-display-date-time): Fix regexp search string. | ||
| 46 | (todos-backward-item, todos-forward-item): Use variable | ||
| 47 | todos-item-start. | ||
| 48 | (todos-add-category): Use todos-check-category-name and | ||
| 49 | todos-categories-alist. | ||
| 50 | (todos-rename-category): Use todos-current-category, | ||
| 51 | todos-check-category-name and todos-categories-alist. | ||
| 52 | (todos-delete-category): Use todos-check-category-name and | ||
| 53 | todos-categories-alist and take done items into account. | ||
| 54 | (todos-insert-item): Use separate arguments to handle insertion of | ||
| 55 | date/dayname and time strings, add new argument to mark item for | ||
| 56 | diary inclusion, use new todos-read-* functions, | ||
| 57 | todos-set-item-priority and todos-item-counts. | ||
| 58 | (todos-insert-item-here, todos-insert-item-for-diary) | ||
| 59 | (todos-insert-item-from-calendar): Adapt to new version of | ||
| 60 | todos-insert-item. | ||
| 61 | (todos-delete-item, todos-item-done): Use todos-item-counts. | ||
| 62 | (todos-edit-item): Indent newlines inserted by C-q C-j if nonspace | ||
| 63 | char follows. | ||
| 64 | (todos-lower-item): Ensure only not-done items can be lowered. | ||
| 65 | (todos-move-item): Use todos-current-category, todos-read-category | ||
| 66 | and todos-item-counts. | ||
| 67 | (todos-archive-done-items): Use todos-current-category and | ||
| 68 | todos-item-counts; fix regexp search string. | ||
| 69 | (todos-item-undo): Use todos-current-category, | ||
| 70 | todos-set-item-priority, todos-insert-with-overlays and | ||
| 71 | todos-item-counts; restore if user quits before inserting undone | ||
| 72 | item. | ||
| 73 | (todos-date-pattern): Rewrite without using | ||
| 74 | todos-date-nodayname-pattern and todos-dayname-date-pattern. | ||
| 75 | (todos-date-string-match): Do not make todos-date-pattern an | ||
| 76 | unnumbered group. | ||
| 77 | (todos-time-string-match): Make todos-date-pattern a shy group. | ||
| 78 | (todos-wrap-and-indent): Use todos-indent-to-here. | ||
| 79 | (todos-reset-prefix): Revert to using todos-show instead of | ||
| 80 | todos-category-select. | ||
| 81 | (todos-prefix-overlays): Fix numbering of done items and updating | ||
| 82 | of prefix. | ||
| 83 | (todos-category-select): Use todos-current-category; fix display | ||
| 84 | of separator string; don't move point to top of category. | ||
| 85 | (todos-jump-to-category-noninteractively): Use todos-category-number. | ||
| 86 | (todos-insert-with-overlays): Use todos-item-start unconditionally. | ||
| 87 | (todos-item-start): New variable. | ||
| 88 | (todos-item-start): Use it to define this function. | ||
| 89 | (todos-item-end): Adjust if item is last unfinished one before | ||
| 90 | displayed done items. | ||
| 91 | (todos-remove-item): Use todos-item-start and todos-item-end | ||
| 92 | instead of todos-forward-item and todos-backward-item. | ||
| 93 | (todos-longest-category-name-length): Add argument for list of | ||
| 94 | categories. | ||
| 95 | |||
| 96 | 2012-09-13 Stephen Berman <stephen.berman@gmx.net> | ||
| 97 | |||
| 3 | * calendar/todos.el: Comment out calendar require, since diary-lib | 98 | * calendar/todos.el: Comment out calendar require, since diary-lib |
| 4 | requires calendar. Rearrange file to group definitions according | 99 | requires calendar. Rearrange file to group definitions according |
| 5 | to their use (types of commands, internal functions, etc.) | 100 | to their use (types of commands, internal functions, etc.) |
| @@ -10,9 +105,8 @@ | |||
| 10 | (todos-current-date, todos-item-end-overlays) | 105 | (todos-current-date, todos-item-end-overlays) |
| 11 | (todos-list-categories): Remove. | 106 | (todos-list-categories): Remove. |
| 12 | (todos-item-end): Remove (the variable, not the function). | 107 | (todos-item-end): Remove (the variable, not the function). |
| 13 | (todos-item-overlays): Rename to todos-prefix-overlays and adjust | 108 | (todos-prefix-overlays): Rename from todos-item-overlays and adjust |
| 14 | callers. | 109 | callers. |
| 15 | (todos-prefix-overlays): Rename from todos-item-overlays. | ||
| 16 | (todos-done-separator, todos-done-string, todos-show-with-done) | 110 | (todos-done-separator, todos-done-string, todos-show-with-done) |
| 17 | (todos-files, todos-archive-file, todos-categories-buffer) | 111 | (todos-files, todos-archive-file, todos-categories-buffer) |
| 18 | (todos-archived-categories-buffer, todos-wrap-lines) | 112 | (todos-archived-categories-buffer, todos-wrap-lines) |
| @@ -35,7 +129,7 @@ | |||
| 35 | (todos-insert-item-from-calendar, todos-edit-quit) | 129 | (todos-insert-item-from-calendar, todos-edit-quit) |
| 36 | (todos-change-date, todos-item-done, todos-archive-done-items) | 130 | (todos-change-date, todos-item-done, todos-archive-done-items) |
| 37 | (todos-item-undo): New commands. | 131 | (todos-item-undo): New commands. |
| 38 | (todos-archive-mode): New mode. | 132 | (todos-archive-mode): New major mode. |
| 39 | (todos-archive-mode-map, todos-edit-mode-map): New keymaps. | 133 | (todos-archive-mode-map, todos-edit-mode-map): New keymaps. |
| 40 | (todos-category-beg): Change value. | 134 | (todos-category-beg): Change value. |
| 41 | (todos-number-prefix): Change default value. | 135 | (todos-number-prefix): Change default value. |
diff --git a/lisp/calendar/todos.el b/lisp/calendar/todos.el index c4788044520..7ec54e0f2e9 100644 --- a/lisp/calendar/todos.el +++ b/lisp/calendar/todos.el | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | ;;; todos.el --- major mode for editing TODO list files | 1 | ;;; Todos.el --- major mode for displaying and editing Todo lists |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1997, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, | 3 | ;; Copyright (C) 1997, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, |
| 4 | ;; 2008, 2009 Free Software Foundation, Inc. | 4 | ;; 2008, 2009 Free Software Foundation, Inc. |
| @@ -48,7 +48,7 @@ | |||
| 48 | ;; | 48 | ;; |
| 49 | ;; Preface, Quickstart Installation | 49 | ;; Preface, Quickstart Installation |
| 50 | ;; | 50 | ;; |
| 51 | ;; To get this to work, make emacs execute the line | 51 | ;; To get this to work, make Emacs execute the line |
| 52 | ;; | 52 | ;; |
| 53 | ;; (autoload 'todos "todos" | 53 | ;; (autoload 'todos "todos" |
| 54 | ;; "Major mode for editing TODO lists." t) | 54 | ;; "Major mode for editing TODO lists." t) |
| @@ -237,7 +237,7 @@ | |||
| 237 | ;; o GNATS support | 237 | ;; o GNATS support |
| 238 | ;; o elide multiline (as in bbdb, or, to a lesser degree, in | 238 | ;; o elide multiline (as in bbdb, or, to a lesser degree, in |
| 239 | ;; outline mode) | 239 | ;; outline mode) |
| 240 | ;; o rewrite complete package to store data as lisp objects | 240 | ;; o rewrite complete package to store data as Lisp objects |
| 241 | ;; and have display modes for display, for diary export, | 241 | ;; and have display modes for display, for diary export, |
| 242 | ;; etc. (Richard Stallman pointed out this is a bad idea) | 242 | ;; etc. (Richard Stallman pointed out this is a bad idea) |
| 243 | ;; o so base todos.el on generic-mode.el instead | 243 | ;; o so base todos.el on generic-mode.el instead |
| @@ -261,8 +261,8 @@ | |||
| 261 | (require 'diary-lib) | 261 | (require 'diary-lib) |
| 262 | 262 | ||
| 263 | ;; --------------------------------------------------------------------------- | 263 | ;; --------------------------------------------------------------------------- |
| 264 | |||
| 265 | ;;; Customizable options | 264 | ;;; Customizable options |
| 265 | |||
| 266 | (defgroup todos nil | 266 | (defgroup todos nil |
| 267 | "Maintain lists of todo items." | 267 | "Maintain lists of todo items." |
| 268 | :link '(emacs-commentary-link "todos") | 268 | :link '(emacs-commentary-link "todos") |
| @@ -275,21 +275,6 @@ | |||
| 275 | :initialize 'custom-initialize-default | 275 | :initialize 'custom-initialize-default |
| 276 | :set 'todos-reset-prefix | 276 | :set 'todos-reset-prefix |
| 277 | :group 'todos) | 277 | :group 'todos) |
| 278 | |||
| 279 | ;; "TODO mode prefix for entries. | ||
| 280 | |||
| 281 | ;; This is useful in conjunction with `calendar' and `diary' if you use | ||
| 282 | |||
| 283 | ;; #include \"~/.todos-do\" | ||
| 284 | |||
| 285 | ;; in your diary file to include your todo list file as part of your | ||
| 286 | ;; diary. With the default value \"*/*\" the diary displays each entry | ||
| 287 | ;; every day and it may also be marked on every day of the calendar. | ||
| 288 | ;; Using \"&%%(equal (calendar-current-date) date)\" instead will only | ||
| 289 | ;; show and mark todo entries for today, but may slow down processing of | ||
| 290 | ;; the diary file somewhat." | ||
| 291 | ;; :type 'string | ||
| 292 | ;; :group 'todos) | ||
| 293 | 278 | ||
| 294 | (defcustom todos-number-prefix t | 279 | (defcustom todos-number-prefix t |
| 295 | "Non-nil to show item prefixes as consecutively increasing integers." | 280 | "Non-nil to show item prefixes as consecutively increasing integers." |
| @@ -298,7 +283,8 @@ | |||
| 298 | :set 'todos-reset-prefix | 283 | :set 'todos-reset-prefix |
| 299 | :group 'todos) | 284 | :group 'todos) |
| 300 | 285 | ||
| 301 | (defcustom todos-done-separator (make-string (window-width) ?-) | 286 | ;; FIXME: length (window-width) causes problems. Also, bad when window-width changes |
| 287 | (defcustom todos-done-separator (make-string (1- (window-width)) ?-) | ||
| 302 | "String used to visual separate done from not done items. | 288 | "String used to visual separate done from not done items. |
| 303 | Displayed in a before-string overlay by `todos-toggle-view-done-items'." | 289 | Displayed in a before-string overlay by `todos-toggle-view-done-items'." |
| 304 | :type 'string | 290 | :type 'string |
| @@ -334,11 +320,6 @@ Displayed in a before-string overlay by `todos-toggle-view-done-items'." | |||
| 334 | :type 'file | 320 | :type 'file |
| 335 | :group 'todos) | 321 | :group 'todos) |
| 336 | 322 | ||
| 337 | ;; (defcustom todos-file-done (convert-standard-filename "~/.emacs.d/.todos-done") | ||
| 338 | ;; "TODO mode archive file." | ||
| 339 | ;; :type 'file | ||
| 340 | ;; :group 'todos) | ||
| 341 | |||
| 342 | (defcustom todos-mode-hook nil | 323 | (defcustom todos-mode-hook nil |
| 343 | "TODO mode hooks." | 324 | "TODO mode hooks." |
| 344 | :type 'hook | 325 | :type 'hook |
| @@ -349,30 +330,13 @@ Displayed in a before-string overlay by `todos-toggle-view-done-items'." | |||
| 349 | :type 'hook | 330 | :type 'hook |
| 350 | :group 'todos) | 331 | :group 'todos) |
| 351 | 332 | ||
| 352 | ;; (defcustom todos-insert-threshold 0 | ||
| 353 | ;; "TODO mode insertion accuracy. | ||
| 354 | |||
| 355 | ;; If you have 8 items in your TODO list, then you may get asked 4 | ||
| 356 | ;; questions by the binary insertion algorithm. However, you may not | ||
| 357 | ;; really have a need for such accurate priorities amongst your TODO | ||
| 358 | ;; items. If you now think about the binary insertion halving the size | ||
| 359 | ;; of the window each time, then the threshold is the window size at | ||
| 360 | ;; which it will stop. If you set the threshold to zero, the upper and | ||
| 361 | ;; lower bound will coincide at the end of the loop and you will insert | ||
| 362 | ;; your item just before that point. If you set the threshold to, | ||
| 363 | ;; e.g. 8, it will stop as soon as the window size drops below that | ||
| 364 | ;; amount and will insert the item in the approximate center of that | ||
| 365 | ;; window." | ||
| 366 | ;; :type 'integer | ||
| 367 | ;; :group 'todos) | ||
| 368 | |||
| 369 | (defcustom todos-categories-buffer "*TODOS Categories*" | 333 | (defcustom todos-categories-buffer "*TODOS Categories*" |
| 370 | "Name of buffer displayed by `todos-display-categories'" | 334 | "Name of buffer displayed by `todos-display-categories'." |
| 371 | :type 'string | 335 | :type 'string |
| 372 | :group 'todos) | 336 | :group 'todos) |
| 373 | 337 | ||
| 374 | (defcustom todos-archived-categories-buffer "*TODOS Archived Categories*" | 338 | (defcustom todos-archived-categories-buffer "*TODOS Archived Categories*" |
| 375 | "Name of buffer displayed by `todos-display-categories'" | 339 | "Name of buffer displayed by `todos-display-categories'." |
| 376 | :type 'string | 340 | :type 'string |
| 377 | :group 'todos) | 341 | :group 'todos) |
| 378 | 342 | ||
| @@ -403,7 +367,7 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 403 | ) | 367 | ) |
| 404 | 368 | ||
| 405 | (defcustom todos-exclusion-end "]" | 369 | (defcustom todos-exclusion-end "]" |
| 406 | "String appended to item date to match todos-exclusion-start." | 370 | "String appended to item date to match `todos-exclusion-start'." |
| 407 | :type 'string | 371 | :type 'string |
| 408 | :group 'todos | 372 | :group 'todos |
| 409 | ;; :initialize 'custom-initialize-default | 373 | ;; :initialize 'custom-initialize-default |
| @@ -426,11 +390,6 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 426 | 0 means print all entries." | 390 | 0 means print all entries." |
| 427 | :type 'integer | 391 | :type 'integer |
| 428 | :group 'todos) | 392 | :group 'todos) |
| 429 | ;; (defcustom todos-remove-separator t | ||
| 430 | ;; "Non-nil to remove category separators in\ | ||
| 431 | ;; \\[todos-top-priorities] and \\[todos-print]." | ||
| 432 | ;; :type 'boolean | ||
| 433 | ;; :group 'todos) | ||
| 434 | 393 | ||
| 435 | (defcustom todos-save-top-priorities-too t | 394 | (defcustom todos-save-top-priorities-too t |
| 436 | "Non-nil makes `todos-save' automatically save top-priorities in `todos-file-top'." | 395 | "Non-nil makes `todos-save' automatically save top-priorities in `todos-file-top'." |
| @@ -438,28 +397,33 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 438 | :group 'todos) | 397 | :group 'todos) |
| 439 | 398 | ||
| 440 | (defcustom todos-completion-ignore-case t ;; FIXME: nil for release | 399 | (defcustom todos-completion-ignore-case t ;; FIXME: nil for release |
| 441 | "Non-nil means don't consider case significant in todos-completing-read." | 400 | "Non-nil means don't consider case significant in `todos-read-category'." |
| 442 | :type 'boolean | 401 | :type 'boolean |
| 443 | :group 'todos) | 402 | :group 'todos) |
| 444 | 403 | ||
| 445 | (defcustom todos-add-time-string t | 404 | (defcustom todos-always-add-time-string t |
| 446 | "Add current time to date string inserted in front of new items." | 405 | "Add current time to date string inserted in front of new items." |
| 447 | :type 'boolean | 406 | :type 'boolean |
| 448 | :group 'todos) | 407 | :group 'todos) |
| 449 | 408 | ||
| 450 | (defcustom todos-wrap-lines t | 409 | (defcustom todos-wrap-lines t |
| 451 | "" ;FIXME | 410 | "" |
| 452 | :group 'todos | 411 | :group 'todos |
| 453 | :type 'boolean) | 412 | :type 'boolean) |
| 454 | 413 | ||
| 455 | (defcustom todos-line-wrapping-function 'todos-wrap-and-indent | 414 | (defcustom todos-line-wrapping-function 'todos-wrap-and-indent |
| 456 | "" ;FIXME | 415 | "" |
| 457 | :group 'todos | 416 | :group 'todos |
| 458 | :type 'function) | 417 | :type 'function) |
| 459 | 418 | ||
| 460 | ;; --------------------------------------------------------------------------- | 419 | (defcustom todos-indent-to-here 6 |
| 420 | "" | ||
| 421 | :type 'integer | ||
| 422 | :group 'todos) | ||
| 461 | 423 | ||
| 424 | ;; --------------------------------------------------------------------------- | ||
| 462 | ;;; Faces | 425 | ;;; Faces |
| 426 | |||
| 463 | (defface todos-prefix-string | 427 | (defface todos-prefix-string |
| 464 | '((t | 428 | '((t |
| 465 | :inherit font-lock-constant-face | 429 | :inherit font-lock-constant-face |
| @@ -467,6 +431,13 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 467 | "Face for Todos prefix string." | 431 | "Face for Todos prefix string." |
| 468 | :group 'todos) | 432 | :group 'todos) |
| 469 | 433 | ||
| 434 | (defface todos-button | ||
| 435 | '((t | ||
| 436 | :inherit tool-bar | ||
| 437 | )) | ||
| 438 | "Face for buttons in todos-display-categories." | ||
| 439 | :group 'todos) | ||
| 440 | |||
| 470 | (defface todos-date | 441 | (defface todos-date |
| 471 | '((t | 442 | '((t |
| 472 | :inherit diary | 443 | :inherit diary |
| @@ -508,17 +479,11 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 508 | "Font-locking for Todos mode.") | 479 | "Font-locking for Todos mode.") |
| 509 | 480 | ||
| 510 | ;; --------------------------------------------------------------------------- | 481 | ;; --------------------------------------------------------------------------- |
| 482 | ;;; Mode setup | ||
| 511 | 483 | ||
| 512 | ;;; Internal variables | ||
| 513 | (defvar todos-categories nil | 484 | (defvar todos-categories nil |
| 514 | "TODO categories.") | 485 | "TODO categories.") |
| 515 | 486 | ||
| 516 | (defvar todos-previous-line 0 | ||
| 517 | "Previous line asked about.") | ||
| 518 | |||
| 519 | (defvar todos-previous-answer 0 | ||
| 520 | "Previous answer got.") | ||
| 521 | |||
| 522 | (defvar todos-mode-map | 487 | (defvar todos-mode-map |
| 523 | (let ((map (make-keymap))) | 488 | (let ((map (make-keymap))) |
| 524 | (suppress-keymap map t) | 489 | (suppress-keymap map t) |
| @@ -549,10 +514,10 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 549 | (define-key map "e" 'todos-edit-item) | 514 | (define-key map "e" 'todos-edit-item) |
| 550 | (define-key map "E" 'todos-edit-multiline) | 515 | (define-key map "E" 'todos-edit-multiline) |
| 551 | ;; (define-key map "" 'todos-change-date) | 516 | ;; (define-key map "" 'todos-change-date) |
| 552 | ;; (define-key map "f" 'todos-file-item) | 517 | ;; (define-key map "f" 'todos-file-item) |
| 553 | (define-key map "ii" 'todos-insert-item) | 518 | (define-key map "ii" 'todos-insert-item) |
| 554 | (define-key map "ih" 'todos-insert-item-here) | 519 | (define-key map "ih" 'todos-insert-item-here) |
| 555 | (define-key map "ia" 'todos-insert-item-ask-date) | 520 | (define-key map "ia" 'todos-insert-item-ask-date-time) |
| 556 | (define-key map "id" 'todos-insert-item-for-diary) | 521 | (define-key map "id" 'todos-insert-item-for-diary) |
| 557 | ;; (define-key map "in" 'todos-insert-item-no-time) | 522 | ;; (define-key map "in" 'todos-insert-item-no-time) |
| 558 | (define-key map "k" 'todos-delete-item) | 523 | (define-key map "k" 'todos-delete-item) |
| @@ -563,6 +528,7 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 563 | (define-key map "u" 'todos-item-undo) | 528 | (define-key map "u" 'todos-item-undo) |
| 564 | (define-key map "y" 'todos-toggle-item-diary-inclusion) | 529 | (define-key map "y" 'todos-toggle-item-diary-inclusion) |
| 565 | ;; (define-key map "" 'todos-toggle-diary-inclusion) | 530 | ;; (define-key map "" 'todos-toggle-diary-inclusion) |
| 531 | (define-key map [remap newline] 'newline-and-indent) | ||
| 566 | map) | 532 | map) |
| 567 | "Todos mode keymap.") | 533 | "Todos mode keymap.") |
| 568 | 534 | ||
| @@ -588,11 +554,26 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 588 | "Todos Archive mode keymap.") | 554 | "Todos Archive mode keymap.") |
| 589 | 555 | ||
| 590 | (defvar todos-edit-mode-map | 556 | (defvar todos-edit-mode-map |
| 591 | (let ((map (make-keymap))) | 557 | (let ((map (make-sparse-keymap))) |
| 592 | (define-key map "\C-c\C-q" 'todos-edit-quit) | 558 | (define-key map "\C-c\C-q" 'todos-edit-quit) |
| 559 | (define-key map [remap newline] 'newline-and-indent) | ||
| 593 | map) | 560 | map) |
| 594 | "Todos Edit mode keymap.") | 561 | "Todos Edit mode keymap.") |
| 595 | 562 | ||
| 563 | (defvar todos-categories-mode-map | ||
| 564 | (let ((map (make-sparse-keymap))) | ||
| 565 | (suppress-keymap map t) | ||
| 566 | (define-key map "a" 'todos-display-categories-alphabetically) | ||
| 567 | (define-key map "c" 'todos-display-categories) | ||
| 568 | (define-key map "l" 'todos-lower-category) | ||
| 569 | (define-key map "r" 'todos-raise-category) | ||
| 570 | (define-key map "q" 'bury-buffer) ;FIXME ? | ||
| 571 | ;; (define-key map "A" 'todos-add-category) | ||
| 572 | ;; (define-key map "D" 'todos-delete-category) | ||
| 573 | ;; (define-key map "R" 'todos-rename-category) | ||
| 574 | map) | ||
| 575 | "Todos Categories mode keymap.") | ||
| 576 | |||
| 596 | (defvar todos-category-number 0 "TODO category number.") | 577 | (defvar todos-category-number 0 "TODO category number.") |
| 597 | 578 | ||
| 598 | (defvar todos-tmp-buffer-name " *todo tmp*") | 579 | (defvar todos-tmp-buffer-name " *todo tmp*") |
| @@ -600,250 +581,206 @@ Automatically generated when `todos-save-top-priorities' is non-nil." | |||
| 600 | (defvar todos-category-beg "--==-- " | 581 | (defvar todos-category-beg "--==-- " |
| 601 | "Category start separator to be prepended onto category name.") | 582 | "Category start separator to be prepended onto category name.") |
| 602 | 583 | ||
| 603 | ;; --------------------------------------------------------------------------- | 584 | (easy-menu-define todos-menu todos-mode-map "Todo Menu" |
| 585 | '("Todo" | ||
| 586 | ["Next category" todos-forward-category t] | ||
| 587 | ["Previous category" todos-backward-category t] | ||
| 588 | ["Jump to category" todos-jump-to-category t] | ||
| 589 | ["Show top priority items" todos-top-priorities t] | ||
| 590 | ["Print categories" todos-print t] | ||
| 591 | "---" | ||
| 592 | ["Edit item" todos-edit-item t] | ||
| 593 | ["File item" todos-file-item t] | ||
| 594 | ["Insert new item" todos-insert-item t] | ||
| 595 | ["Insert item here" todos-insert-item-here t] | ||
| 596 | ["Kill item" todos-delete-item t] | ||
| 597 | "---" | ||
| 598 | ["Lower item priority" todos-lower-item t] | ||
| 599 | ["Raise item priority" todos-raise-item t] | ||
| 600 | "---" | ||
| 601 | ["Next item" todos-forward-item t] | ||
| 602 | ["Previous item" todos-backward-item t] | ||
| 603 | "---" | ||
| 604 | ["Save" todos-save t] | ||
| 605 | ["Save Top Priorities" todos-save-top-priorities t] | ||
| 606 | "---" | ||
| 607 | ["Quit" todos-quit t] | ||
| 608 | )) | ||
| 604 | 609 | ||
| 605 | ;;; Commands | 610 | ;; As calendar reads .todos-do before todos-mode is loaded. |
| 611 | ;;;###autoload | ||
| 612 | (defun todos-mode () | ||
| 613 | "Major mode for displaying, navigating and editing Todo lists. | ||
| 606 | 614 | ||
| 607 | ;;; Navigation | 615 | \\{todos-mode-map}" |
| 616 | (interactive) | ||
| 617 | (kill-all-local-variables) | ||
| 618 | (setq major-mode 'todos-mode) | ||
| 619 | (setq mode-name "TODOS") | ||
| 620 | (use-local-map todos-mode-map) | ||
| 621 | (easy-menu-add todos-menu) | ||
| 622 | (when todos-wrap-lines (funcall todos-line-wrapping-function)) | ||
| 623 | (make-local-variable 'indent-line-function) | ||
| 624 | (setq indent-line-function 'todos-indent) | ||
| 625 | (make-local-variable 'font-lock-defaults) | ||
| 626 | (setq font-lock-defaults '(todos-font-lock-keywords t)) | ||
| 627 | (make-local-variable 'hl-line-range-function) | ||
| 628 | (setq hl-line-range-function | ||
| 629 | (lambda() (when (todos-item-end) | ||
| 630 | (cons (todos-item-start) (todos-item-end))))) | ||
| 631 | ;; (add-hook 'post-command-hook 'todos-show-paren-hack nil t) | ||
| 632 | (add-to-invisibility-spec 'todos) | ||
| 633 | (setq buffer-read-only t) | ||
| 634 | (run-mode-hooks 'todos-mode-hook)) | ||
| 608 | 635 | ||
| 609 | (defun todos-forward-category () | 636 | (defun todos-archive-mode () |
| 610 | "Go forward to TODO list of next category." | 637 | "Major mode for archived Todos categories. |
| 638 | |||
| 639 | \\{todos-archive-mode-map}" | ||
| 611 | (interactive) | 640 | (interactive) |
| 612 | (setq todos-category-number | 641 | (kill-all-local-variables) |
| 613 | (mod (1+ todos-category-number) (length todos-categories))) | 642 | (setq major-mode 'todos-archive-mode) |
| 614 | (todos-category-select)) | 643 | (setq mode-name "TODOS Archive") |
| 644 | (use-local-map todos-archive-mode-map) | ||
| 645 | ;; (easy-menu-add todos-menu) | ||
| 646 | (when todos-wrap-lines (funcall todos-line-wrapping-function)) | ||
| 647 | (make-local-variable 'font-lock-defaults) | ||
| 648 | (setq font-lock-defaults '(todos-font-lock-keywords t)) | ||
| 649 | (make-local-variable 'hl-line-range-function) | ||
| 650 | (setq hl-line-range-function | ||
| 651 | (lambda() (when (todos-item-end) | ||
| 652 | (cons (todos-item-start) (todos-item-end))))) | ||
| 653 | ;; (add-hook 'post-command-hook 'todos-show-paren-hack nil t) | ||
| 654 | (add-to-invisibility-spec 'todos) | ||
| 655 | (run-mode-hooks 'todos-mode-hook)) | ||
| 615 | 656 | ||
| 616 | (defun todos-backward-category () | 657 | (defun todos-edit-mode () |
| 617 | "Go back to TODO list of previous category." | 658 | "Major mode for editing multiline Todo items. |
| 659 | |||
| 660 | \\{todos-edit-mode-map}" | ||
| 618 | (interactive) | 661 | (interactive) |
| 619 | (setq todos-category-number | 662 | (setq major-mode 'todos-edit-mode) |
| 620 | (mod (1- todos-category-number) (length todos-categories))) | 663 | (setq mode-name "TODOS Edit") |
| 621 | (todos-category-select)) | 664 | (use-local-map todos-edit-mode-map) |
| 665 | (make-local-variable 'font-lock-defaults) | ||
| 666 | (setq font-lock-defaults '(todos-font-lock-keywords t)) | ||
| 667 | (make-local-variable 'indent-line-function) | ||
| 668 | (setq indent-line-function 'todos-indent) | ||
| 669 | (when todos-wrap-lines (funcall todos-line-wrapping-function))) | ||
| 622 | 670 | ||
| 623 | ;; FIXME: Document that a non-existing name creates that category, and add | 671 | (defun todos-categories-mode () |
| 624 | ;; y-or-n-p confirmation -- or eliminate this possibility? | 672 | "Major mode for displaying and editing Todos categories. |
| 625 | (defun todos-jump-to-category () | 673 | |
| 626 | "Jump to a category. Default is previous category." | 674 | \\{todos-categories-mode-map}" |
| 627 | (interactive) | 675 | (interactive) |
| 628 | (let ((category (todos-completing-read))) | 676 | (setq major-mode 'todos-categories-mode) |
| 629 | (if (string= "" category) | 677 | (setq mode-name "TODOS Categories") |
| 630 | (setq category (nth todos-category-number todos-categories))) | 678 | (use-local-map todos-categories-mode-map) |
| 631 | (setq todos-category-number | 679 | (make-local-variable 'font-lock-defaults) |
| 632 | (if (member category todos-categories) | 680 | (setq font-lock-defaults '(todos-font-lock-keywords t)) |
| 633 | (- (length todos-categories) | 681 | (setq buffer-read-only t) |
| 634 | (length (member category todos-categories))) | 682 | ) |
| 635 | (todos-add-category category))) | ||
| 636 | ;; (todos-show))) | ||
| 637 | (todos-category-select))) | ||
| 638 | 683 | ||
| 639 | ;; FIXME ? todos-{backward,forward}-item skip over empty line between done and | 684 | (defun todos-save () |
| 640 | ;; not done items (but todos-forward-item gets there when done items are not | 685 | "Save the TODO list." |
| 641 | ;; displayed) | 686 | (interactive) |
| 642 | (defun todos-backward-item (&optional count) | 687 | (save-excursion |
| 643 | "Select previous entry of TODO list." | 688 | (save-restriction |
| 644 | (interactive "P") | 689 | (save-buffer))) |
| 645 | ;; FIXME ? this moves to bob if on the first item (but so does previous-line) | 690 | ;; (if todos-save-top-priorities-too (todos-save-top-priorities))) |
| 646 | (todos-item-start) | 691 | ) |
| 647 | (unless (bobp) | ||
| 648 | (re-search-backward (concat "^\\(\\[\\(" (regexp-quote todos-done-string) | ||
| 649 | "\\)?\\)?\\(" todos-date-pattern "\\)") | ||
| 650 | nil t (or count 1)))) | ||
| 651 | 692 | ||
| 652 | (defun todos-forward-item (&optional count) | 693 | (defun todos-quit () |
| 653 | "Select COUNT-th next entry of TODO list." | 694 | "Done with TODO list for now." |
| 654 | (interactive "P") | 695 | (interactive) |
| 655 | (goto-char (line-end-position)) | 696 | (widen) |
| 656 | (if (re-search-forward (concat "^\\(\\[\\(" (regexp-quote todos-done-string) | 697 | (todos-save) |
| 657 | "\\)?\\)?\\(" todos-date-pattern "\\)") | 698 | (message "") |
| 658 | nil t (or count 1)) | 699 | (bury-buffer)) |
| 659 | (goto-char (match-beginning 0)) | ||
| 660 | (goto-char (point-max)))) | ||
| 661 | 700 | ||
| 662 | ;; (defun todos-forward-item (&optional count) | 701 | ;; --------------------------------------------------------------------------- |
| 663 | ;; "Select COUNT-th next entry of TODO list." | 702 | ;;; Commands |
| 664 | ;; (interactive "P") | ||
| 665 | ;; (let ((opoint (point)) | ||
| 666 | ;; (done (save-excursion | ||
| 667 | ;; (if (re-search-forward (concat "\n\n\\\(\\[" | ||
| 668 | ;; (regexp-quote todos-done-string) | ||
| 669 | ;; "\\)") nil t) | ||
| 670 | ;; (match-beginning 1))))) | ||
| 671 | ;; ;; FIXME: can this be simplified? | ||
| 672 | ;; (if (looking-at (concat "^\\(\\[\\(" (regexp-quote todos-done-string) "\\)?\\)?" | ||
| 673 | ;; todos-date-pattern)) ; on item header | ||
| 674 | ;; (re-search-forward (concat "^\\(\\[\\(" (regexp-quote todos-done-string) | ||
| 675 | ;; "\\)?\\)?\\(" todos-date-pattern "\\)") | ||
| 676 | ;; nil t (if count (1+ count) 2)) | ||
| 677 | ;; (re-search-forward (concat "^\\(\\[\\(" (regexp-quote todos-done-string) | ||
| 678 | ;; "\\)?\\)?\\(" todos-date-pattern "\\)") | ||
| 679 | ;; nil t (or count 1))) | ||
| 680 | ;; (cond ((save-excursion | ||
| 681 | ;; (goto-char opoint) | ||
| 682 | ;; (looking-at "^$")) ; between done and not done items | ||
| 683 | ;; (forward-line 0)) | ||
| 684 | ;; ((and done (> (point) done)) | ||
| 685 | ;; (forward-line -1)) ; FIXME: count ? | ||
| 686 | ;; ((eq (point) opoint) ; on last item | ||
| 687 | ;; (goto-char (point-max))) | ||
| 688 | ;; (t | ||
| 689 | ;; (goto-char (match-beginning 0)))))) | ||
| 690 | 703 | ||
| 691 | (defvar todos-search-string nil | 704 | ;;; Display |
| 692 | "" ;FIXME | 705 | |
| 693 | ) | 706 | ;;;###autoload |
| 694 | (defun todos-search () | 707 | (defun todos-show () |
| 695 | "" ;FIXME | 708 | "Show TODO list." |
| 696 | (interactive) | 709 | (interactive) |
| 697 | (let ((regex (read-from-minibuffer "Enter a search string (regexp): ")) | 710 | ;; Make this a no-op if called interactively in narrowed Todos mode, since |
| 698 | (start (point)) | 711 | ;; it is in that case redundant, but in particular to work around the bug of |
| 699 | found cat in-done) | 712 | ;; item prefix reduplication with show-paren-mode enabled. |
| 700 | (widen) | 713 | (unless (and (called-interactively-p) |
| 701 | (goto-char (point-min)) | 714 | (eq major-mode 'todos-mode) |
| 702 | (while (and (setq found (re-search-forward regex nil t)) | 715 | (< (- ( point-max) (point-min)) (buffer-size))) |
| 703 | (save-excursion | 716 | ;; Call todos-initial-setup only if there is neither a Todo file nor |
| 704 | (goto-char (line-beginning-position)) | 717 | ;; a corresponding unsaved buffer. |
| 705 | (looking-at (concat "^" (regexp-quote todos-category-beg))))) | 718 | (if (or (file-exists-p todos-file-do) |
| 706 | (forward-line)) | 719 | (let* ((buf (get-buffer (file-name-nondirectory todos-file-do))) |
| 707 | (if found | 720 | (bufname (buffer-file-name buf))) |
| 708 | (progn | 721 | (equal (expand-file-name todos-file-do) bufname))) |
| 709 | (setq found (match-beginning 0)) | 722 | (find-file todos-file-do) |
| 710 | (todos-item-start) | 723 | (todos-initial-setup)) |
| 711 | (when (looking-at (concat "^\\[" (regexp-quote todos-done-string))) | 724 | (unless (eq major-mode 'todos-mode) (todos-mode)) |
| 712 | (setq in-done t)) | 725 | (unless todos-categories-alist |
| 713 | (re-search-backward (concat "^" (regexp-quote todos-category-beg) | 726 | (setq todos-categories-alist (todos-make-categories-alist))) |
| 714 | "\\(.*\\)\n") nil t) | 727 | (unless todos-categories |
| 715 | (setq cat (match-string-no-properties 1)) | 728 | (setq todos-categories (mapcar 'car todos-categories-alist))) |
| 716 | (setq todos-category-number | 729 | (save-excursion |
| 717 | (- (length todos-categories) (length (member cat todos-categories)))) | ||
| 718 | (todos-category-select) | ||
| 719 | (when in-done (unless todos-show-with-done (todos-toggle-view-done-items))) | ||
| 720 | (goto-char found)) | ||
| 721 | (todos-category-select) | 730 | (todos-category-select) |
| 722 | (goto-char start) | 731 | ;; (todos-show-paren-hack) |
| 723 | (message "No match for \"%s\"" regex)))) | 732 | ))) |
| 724 | 733 | ||
| 725 | ;;; Display | 734 | (defun todos-display-categories (&optional alpha) |
| 735 | "Display a numbered list of the Todos category names. | ||
| 736 | The numbers give the order of the categories. | ||
| 726 | 737 | ||
| 727 | (defun todos-display-categories () | 738 | With non-nil ALPHA display a non-numbered alphabetical list. |
| 728 | "Display an alphabetical list of clickable Todos category names. | 739 | The lists are in Todos Categories mode. |
| 729 | Click or type RET on a category name to go to it." | 740 | |
| 741 | The category names are buttonized, and pressing a button displays | ||
| 742 | the category in Todos mode." | ||
| 730 | (interactive) | 743 | (interactive) |
| 731 | (let ((categories (copy-sequence todos-categories)) | 744 | (let ((categories (copy-sequence todos-categories)) |
| 732 | (cat-alist (todos-categories-alist)) | 745 | (num 0)) |
| 733 | (len (todos-longest-category-name-length)) | 746 | (when alpha ;alphabetize the list case insensitively |
| 734 | beg) | 747 | (setq categories (sort categories (lambda (s1 s2) (let ((cis1 (upcase s1)) |
| 735 | ;; alphabetize the list case insensitively | 748 | (cis2 (upcase s2))) |
| 736 | (setq categories (sort categories (lambda (s1 s2) (let ((cis1 (upcase s1)) | 749 | (string< cis1 cis2)))))) |
| 737 | (cis2 (upcase s2))) | ||
| 738 | (string< cis1 cis2))))) | ||
| 739 | (with-current-buffer (get-buffer-create todos-categories-buffer) | 750 | (with-current-buffer (get-buffer-create todos-categories-buffer) |
| 740 | (switch-to-buffer (current-buffer)) | 751 | (switch-to-buffer (current-buffer)) |
| 741 | (erase-buffer) | 752 | (let (buffer-read-only) |
| 742 | (kill-all-local-variables) | 753 | (erase-buffer) |
| 743 | (insert "Press a button to display the corresponding category.\n\n") | 754 | (kill-all-local-variables) |
| 744 | (setq beg (point)) | 755 | (insert "Press a button to display the corresponding category.\n\n") |
| 745 | (mapc (lambda (cat) | 756 | ;; FIXME: abstract format from here and todos-insert-category-name |
| 746 | (let* ((catlen (length cat)) | 757 | (insert (make-string 4 32) (todos-padded-string "Category") |
| 747 | (catlen-odd (eq (logand catlen 1) 1)) ; oddp from cl.el | 758 | (make-string 7 32) "Todos Done\n\n") |
| 748 | (padding (/ (- len catlen) 2))) | 759 | (save-excursion |
| 749 | (insert-button (concat (make-string padding 32) cat | 760 | (mapc '(lambda (cat) (todos-insert-category-name cat alpha)) categories))) |
| 750 | (make-string (if catlen-odd | 761 | (todos-categories-mode)))) |
| 751 | (1+ padding) | 762 | |
| 752 | padding) | 763 | (defun todos-display-categories-alphabetically () |
| 753 | 32)) | 764 | "" |
| 754 | 'face 'tool-bar | 765 | (interactive) |
| 755 | 'action | 766 | (todos-display-categories t)) |
| 756 | `(lambda (button) | ||
| 757 | (todos-jump-to-category-noninteractively ,cat))) | ||
| 758 | (insert (make-string 8 32) | ||
| 759 | "(not done: " | ||
| 760 | (number-to-string (car (cadr (assoc cat cat-alist)))) | ||
| 761 | ", done: " | ||
| 762 | (number-to-string (cdr (cadr (assoc cat cat-alist)))) | ||
| 763 | ")") | ||
| 764 | (newline))) | ||
| 765 | categories)))) | ||
| 766 | ;; (require 'widget) | ||
| 767 | ;; (eval-when-compile | ||
| 768 | ;; (require 'wid-edit)) | ||
| 769 | ;; (with-current-buffer (get-buffer-create todos-categories-buffer) | ||
| 770 | ;; (switch-to-buffer (current-buffer)) | ||
| 771 | ;; (erase-buffer) | ||
| 772 | ;; (kill-all-local-variables) | ||
| 773 | ;; (widget-insert "Press a button to display the corresponding category.\n\n") | ||
| 774 | ;; (setq beg (point)) | ||
| 775 | ;; (mapc (lambda (cat) | ||
| 776 | ;; (widget-create 'push-button | ||
| 777 | ;; :notify (lambda (widget &rest ignore) | ||
| 778 | ;; (todos-jump-to-category-noninteractively | ||
| 779 | ;; (widget-get widget :value))) | ||
| 780 | |||
| 781 | ;; cat) | ||
| 782 | ;; (widget-insert " (not done: " | ||
| 783 | ;; (number-to-string (car (cadr (assoc cat cat-alist)))) | ||
| 784 | ;; ", done: " | ||
| 785 | ;; (number-to-string (cdr (cadr (assoc cat cat-alist)))) | ||
| 786 | ;; ")\n")) | ||
| 787 | ;; categories) | ||
| 788 | ;; (use-local-map widget-keymap) | ||
| 789 | ;; (widget-setup)))) | ||
| 790 | 767 | ||
| 791 | (defun todos-toggle-item-numbering () | 768 | (defun todos-toggle-item-numbering () |
| 792 | "" ;FIXME | 769 | "" |
| 793 | (interactive) | 770 | (interactive) |
| 794 | (todos-reset-prefix 'todos-number-prefix (not todos-number-prefix))) | 771 | (todos-reset-prefix 'todos-number-prefix (not todos-number-prefix))) |
| 795 | 772 | ||
| 796 | (defun todos-toggle-view-done-items () | 773 | (defun todos-toggle-view-done-items () |
| 797 | "" ; FIXME | 774 | "" |
| 798 | (interactive) | 775 | (interactive) |
| 799 | (let ((beg (point-min)) | 776 | (save-excursion |
| 800 | (done-sep (if (string-match "^[[:space:]]*$" todos-done-separator) | 777 | (goto-char (point-min)) |
| 801 | todos-done-separator | 778 | (let ((todos-show-with-done |
| 802 | (propertize (concat todos-done-separator "\n") | 779 | (if (re-search-forward (concat "\n\\(\\[" (regexp-quote todos-done-string) |
| 803 | 'face 'todos-done-sep))) | 780 | "\\)") nil t) |
| 804 | (todos-show-with-done nil) | 781 | nil |
| 805 | (done (point-max)) | 782 | t))) |
| 806 | end ov) | 783 | (todos-category-select)))) |
| 807 | (save-excursion | ||
| 808 | (goto-char beg) | ||
| 809 | (if (re-search-forward (concat "\n\\[" (regexp-quote todos-done-string)) | ||
| 810 | nil t) | ||
| 811 | ;; hide done items | ||
| 812 | (progn (setq end (match-beginning 0)) | ||
| 813 | (narrow-to-region beg end)) | ||
| 814 | (widen) | ||
| 815 | (re-search-forward (concat "^" (regexp-quote todos-category-beg)) nil t) | ||
| 816 | (setq end (or (match-beginning 0) (point-max))) | ||
| 817 | (goto-char beg) | ||
| 818 | (if (re-search-forward | ||
| 819 | (concat (if (eq beg done) "" "\n") ; no newline if no unfinished items | ||
| 820 | "\n\\(\\[" (regexp-quote todos-done-string) "\\)") | ||
| 821 | end t) | ||
| 822 | ;; show done items | ||
| 823 | (let ((prefix (propertize | ||
| 824 | (concat (if todos-number-prefix "1" todos-prefix) " ") | ||
| 825 | 'face 'todos-prefix-string)) | ||
| 826 | ov-done ov-pref) | ||
| 827 | (setq done (match-beginning 1)) | ||
| 828 | (narrow-to-region beg end) | ||
| 829 | (todos-prefix-overlays) | ||
| 830 | ;; add non-empty separator overlay in front of prefix overlay on | ||
| 831 | ;; first done item | ||
| 832 | (unless (string= done-sep todos-done-separator) | ||
| 833 | (goto-char done) | ||
| 834 | (remove-overlays done done) | ||
| 835 | ;; must make separator overlay after making prefix overlay to | ||
| 836 | ;; get the order separator before prefix | ||
| 837 | (setq ov-pref (make-overlay done done) | ||
| 838 | ov-done (make-overlay done done)) | ||
| 839 | (overlay-put ov-pref 'before-string prefix) | ||
| 840 | (overlay-put ov-done 'before-string done-sep))) | ||
| 841 | ;; (when (setq ov (car (overlays-in done done))) | ||
| 842 | ;; (when (equal (overlay-get ov 'before-string) done-sep) | ||
| 843 | ;; (push ov todos-done-overlays) | ||
| 844 | ;; (delete-overlay ov))) | ||
| 845 | (todos-category-select) | ||
| 846 | (error "No done items in this category")))))) | ||
| 847 | 784 | ||
| 848 | (defun todos-view-archive (&optional cat) | 785 | (defun todos-view-archive (&optional cat) |
| 849 | "" | 786 | "" |
| @@ -858,7 +795,7 @@ Click or type RET on a category name to go to it." | |||
| 858 | (todos-category-select))) | 795 | (todos-category-select))) |
| 859 | (error "There is currently no Todos archive"))) | 796 | (error "There is currently no Todos archive"))) |
| 860 | 797 | ||
| 861 | ;; FIXME: very slow | 798 | ;; FIXME: slow |
| 862 | (defun todos-diary-items () | 799 | (defun todos-diary-items () |
| 863 | "Display all todo items marked for diary inclusion." | 800 | "Display all todo items marked for diary inclusion." |
| 864 | (interactive) | 801 | (interactive) |
| @@ -911,7 +848,7 @@ Click or type RET on a category name to go to it." | |||
| 911 | 848 | ||
| 912 | ;; FIXME: make this a customizable option for whole Todos file | 849 | ;; FIXME: make this a customizable option for whole Todos file |
| 913 | (defun todos-toggle-display-date-time () | 850 | (defun todos-toggle-display-date-time () |
| 914 | "" ; FIXME | 851 | "" |
| 915 | (interactive) | 852 | (interactive) |
| 916 | (save-excursion | 853 | (save-excursion |
| 917 | (goto-char (point-min)) | 854 | (goto-char (point-min)) |
| @@ -926,13 +863,15 @@ Click or type RET on a category name to go to it." | |||
| 926 | (if hidden (remove-overlays (point-min) (point-max) 'display "") | 863 | (if hidden (remove-overlays (point-min) (point-max) 'display "") |
| 927 | (while (not (eobp)) | 864 | (while (not (eobp)) |
| 928 | (re-search-forward (concat "^\\[?" todos-date-pattern | 865 | (re-search-forward (concat "^\\[?" todos-date-pattern |
| 929 | " \\(" diary-time-regexp "\\)?\\]? ") | 866 | "\\( " diary-time-regexp "\\)?\\]? ") |
| 930 | ; FIXME: this space in header? ^ | 867 | ; FIXME: this space in header? ^ |
| 931 | nil t) | 868 | nil t) |
| 932 | (setq ov (make-overlay (match-beginning 0) (match-end 0) nil t)) | 869 | (setq ov (make-overlay (match-beginning 0) (match-end 0) nil t)) |
| 933 | (overlay-put ov 'display "") | 870 | (overlay-put ov 'display "") |
| 934 | (forward-line))) | 871 | (forward-line))) |
| 935 | (todos-update-numbered-prefix)))) | 872 | ;; FIXME: need this? |
| 873 | ;; (todos-update-numbered-prefix) | ||
| 874 | ))) | ||
| 936 | 875 | ||
| 937 | ;;;###autoload | 876 | ;;;###autoload |
| 938 | (defun todos-top-priorities (&optional nof-priorities category-pr-page show-done) | 877 | (defun todos-top-priorities (&optional nof-priorities category-pr-page show-done) |
| @@ -1004,6 +943,91 @@ With non-nil SHOW-DONE, include done items in the listing." | |||
| 1004 | (message "Type C-x 1 to remove %s window. M-C-v to scroll the help." | 943 | (message "Type C-x 1 to remove %s window. M-C-v to scroll the help." |
| 1005 | todos-print-buffer-name))) | 944 | todos-print-buffer-name))) |
| 1006 | 945 | ||
| 946 | ;;; Navigation | ||
| 947 | |||
| 948 | (defun todos-forward-category () | ||
| 949 | "Go forward to TODO list of next category." | ||
| 950 | (interactive) | ||
| 951 | (setq todos-category-number | ||
| 952 | (mod (1+ todos-category-number) (length todos-categories))) | ||
| 953 | (todos-category-select)) | ||
| 954 | |||
| 955 | (defun todos-backward-category () | ||
| 956 | "Go back to TODO list of previous category." | ||
| 957 | (interactive) | ||
| 958 | (setq todos-category-number | ||
| 959 | (mod (1- todos-category-number) (length todos-categories))) | ||
| 960 | (todos-category-select)) | ||
| 961 | |||
| 962 | ;; FIXME: Document that a non-existing name creates that category, and add | ||
| 963 | ;; y-or-n-p confirmation -- or eliminate this possibility? | ||
| 964 | (defun todos-jump-to-category () | ||
| 965 | "Jump to a category. Default is previous category." | ||
| 966 | (interactive) | ||
| 967 | (let ((category (todos-read-category))) | ||
| 968 | (if (string= "" category) | ||
| 969 | (setq category (todos-current-category))) | ||
| 970 | (setq todos-category-number | ||
| 971 | (if (member category todos-categories) | ||
| 972 | (- (length todos-categories) | ||
| 973 | (length (member category todos-categories))) | ||
| 974 | (todos-add-category category))) | ||
| 975 | ;; (todos-show))) | ||
| 976 | (todos-category-select))) | ||
| 977 | |||
| 978 | ;; FIXME ? todos-{backward,forward}-item skip over empty line between done and | ||
| 979 | ;; not done items (but todos-forward-item gets there when done items are not | ||
| 980 | ;; displayed). Also disallow prefix arg value < 1 (re-search-* allows these) | ||
| 981 | (defun todos-backward-item (&optional count) | ||
| 982 | "Select COUNT-th previous entry of TODO list." | ||
| 983 | (interactive "P") | ||
| 984 | ;; FIXME ? this moves to bob if on the first item (but so does previous-line) | ||
| 985 | (todos-item-start) | ||
| 986 | (unless (bobp) | ||
| 987 | (re-search-backward todos-item-start nil t (or count 1)))) | ||
| 988 | |||
| 989 | (defun todos-forward-item (&optional count) | ||
| 990 | "Select COUNT-th next entry of TODO list." | ||
| 991 | (interactive "P") | ||
| 992 | (goto-char (line-end-position)) | ||
| 993 | (if (re-search-forward todos-item-start nil t (or count 1)) | ||
| 994 | (goto-char (match-beginning 0)) | ||
| 995 | (goto-char (point-max)))) | ||
| 996 | |||
| 997 | ;; FIXME: continue search with same regexp | ||
| 998 | (defvar todos-search-string nil | ||
| 999 | "" | ||
| 1000 | ) | ||
| 1001 | (defun todos-search () | ||
| 1002 | "" | ||
| 1003 | (interactive) | ||
| 1004 | (let ((regex (read-from-minibuffer "Enter a search string (regexp): ")) | ||
| 1005 | (start (point)) | ||
| 1006 | found cat in-done) | ||
| 1007 | (widen) | ||
| 1008 | (goto-char (point-min)) | ||
| 1009 | (while (and (setq found (re-search-forward regex nil t)) | ||
| 1010 | (save-excursion | ||
| 1011 | (goto-char (line-beginning-position)) | ||
| 1012 | (looking-at (concat "^" (regexp-quote todos-category-beg))))) | ||
| 1013 | (forward-line)) | ||
| 1014 | (if found | ||
| 1015 | (progn | ||
| 1016 | (setq found (match-beginning 0)) | ||
| 1017 | (todos-item-start) | ||
| 1018 | (when (looking-at (concat "^\\[" (regexp-quote todos-done-string))) | ||
| 1019 | (setq in-done t)) | ||
| 1020 | (re-search-backward (concat "^" (regexp-quote todos-category-beg) | ||
| 1021 | "\\(.*\\)\n") nil t) | ||
| 1022 | (setq cat (match-string-no-properties 1)) | ||
| 1023 | (todos-category-number cat) | ||
| 1024 | (todos-category-select) | ||
| 1025 | (when in-done (unless todos-show-with-done (todos-toggle-view-done-items))) | ||
| 1026 | (goto-char found)) | ||
| 1027 | (todos-category-select) | ||
| 1028 | (goto-char start) | ||
| 1029 | (message "No match for \"%s\"" regex)))) | ||
| 1030 | |||
| 1007 | ;;; Editing | 1031 | ;;; Editing |
| 1008 | 1032 | ||
| 1009 | ;;;###autoload | 1033 | ;;;###autoload |
| @@ -1011,25 +1035,18 @@ With non-nil SHOW-DONE, include done items in the listing." | |||
| 1011 | "Add new category CAT to the TODO list." | 1035 | "Add new category CAT to the TODO list." |
| 1012 | (interactive) | 1036 | (interactive) |
| 1013 | (let ((buffer-read-only) | 1037 | (let ((buffer-read-only) |
| 1014 | (buf (find-file-noselect todos-file-do t)) | 1038 | (buf (find-file-noselect todos-file-do t))) |
| 1015 | (prompt "Category: ")) | ||
| 1016 | (unless (zerop (buffer-size buf)) | 1039 | (unless (zerop (buffer-size buf)) |
| 1017 | (and (null todos-categories) | 1040 | (and (null todos-categories) |
| 1018 | (error "Error in %s: File is non-empty but contains no category" | 1041 | (error "Error in %s: File is non-empty but contains no category" |
| 1019 | todos-file-do))) | 1042 | todos-file-do))) |
| 1020 | (unless cat (setq cat (read-from-minibuffer prompt))) | 1043 | (unless cat (setq cat (read-from-minibuffer "Category: "))) |
| 1021 | (with-current-buffer buf | 1044 | (with-current-buffer buf |
| 1022 | ;; reject names that could induce bugs and confusion | 1045 | (setq cat (todos-check-category-name cat)) |
| 1023 | (while (and (cond ((string= "" cat) | ||
| 1024 | (setq prompt "Enter a non-empty category name: ")) | ||
| 1025 | ((string-match "\\`\\s-+\\'" cat) | ||
| 1026 | (setq prompt "Enter a category name that is not only white space: ")) | ||
| 1027 | ((member cat todos-categories) | ||
| 1028 | (setq prompt "Enter a non-existing category name: "))) | ||
| 1029 | (setq cat (read-from-minibuffer prompt)))) | ||
| 1030 | ;; initialize a newly created Todo buffer for Todo mode | 1046 | ;; initialize a newly created Todo buffer for Todo mode |
| 1031 | (unless (file-exists-p todos-file-do) (todos-mode)) | 1047 | (unless (file-exists-p todos-file-do) (todos-mode)) |
| 1032 | (setq todos-categories (cons cat todos-categories)) | 1048 | (push cat todos-categories) |
| 1049 | (push (list cat (cons 0 0)) todos-categories-alist) | ||
| 1033 | (widen) | 1050 | (widen) |
| 1034 | (goto-char (point-min)) | 1051 | (goto-char (point-min)) |
| 1035 | ;; make sure file does not begin with empty lines (shouldn't, but may be | 1052 | ;; make sure file does not begin with empty lines (shouldn't, but may be |
| @@ -1042,33 +1059,24 @@ With non-nil SHOW-DONE, include done items in the listing." | |||
| 1042 | (progn (setq todos-category-number 0) (todos-show)) | 1059 | (progn (setq todos-category-number 0) (todos-show)) |
| 1043 | 0)))) | 1060 | 0)))) |
| 1044 | 1061 | ||
| 1045 | ;; FIXME: use function for category name choice here and in todos-add-category | ||
| 1046 | (defun todos-rename-category () | 1062 | (defun todos-rename-category () |
| 1047 | "Rename current Todos category." | 1063 | "Rename current Todos category." |
| 1048 | (interactive) | 1064 | (interactive) |
| 1049 | (let* ((buffer-read-only) | 1065 | (let* ((buffer-read-only) |
| 1050 | (cat (nth todos-category-number todos-categories)) | 1066 | (cat (todos-current-category)) |
| 1051 | (vec (vconcat todos-categories)) | 1067 | (vec (vconcat todos-categories)) |
| 1052 | (new (read-from-minibuffer (format "Rename category \"%s\" to: " cat))) | 1068 | (new (read-from-minibuffer (format "Rename category \"%s\" to: " cat)))) |
| 1053 | prompt) | 1069 | (setq new (todos-check-category-name new)) |
| 1054 | (while (and (cond ((string= "" new) | 1070 | (aset vec todos-category-number new) |
| 1055 | (setq prompt "Enter a non-empty category name: ")) | ||
| 1056 | ((string-match "\\`\\s-+\\'" new) | ||
| 1057 | (setq prompt "Enter a category name that is not only white space: ")) | ||
| 1058 | ((member new todos-categories) | ||
| 1059 | (setq prompt "Enter a non-existing category name: "))) | ||
| 1060 | (setq new (read-from-minibuffer prompt)))) | ||
| 1061 | (aset vec todos-category-number new) | ||
| 1062 | (setq todos-categories (append vec nil)) | 1071 | (setq todos-categories (append vec nil)) |
| 1072 | (setcar (assoc cat todos-categories-alist) new) | ||
| 1063 | (save-excursion | 1073 | (save-excursion |
| 1064 | (widen) | 1074 | (widen) |
| 1065 | (re-search-backward (concat (regexp-quote todos-category-beg) "\\(" | 1075 | (re-search-backward (concat (regexp-quote todos-category-beg) "\\(" |
| 1066 | (regexp-quote cat) "\\)\n") nil t) | 1076 | (regexp-quote cat) "\\)\n") nil t) |
| 1067 | (replace-match new t t nil 1) | 1077 | (replace-match new t t nil 1) |
| 1068 | (goto-char (point-min)) | 1078 | (goto-char (point-min)) |
| 1069 | (setq mode-line-buffer-identification | 1079 | (setq mode-line-buffer-identification (concat "Category: " new)))) |
| 1070 | (concat "Category: " new)))) | ||
| 1071 | ;; (concat "Category: " (format "%18s" new))))) | ||
| 1072 | (todos-category-select)) | 1080 | (todos-category-select)) |
| 1073 | 1081 | ||
| 1074 | (defun todos-delete-category (&optional arg) | 1082 | (defun todos-delete-category (&optional arg) |
| @@ -1076,11 +1084,13 @@ With non-nil SHOW-DONE, include done items in the listing." | |||
| 1076 | With ARG non-nil delete the category unconditionally, | 1084 | With ARG non-nil delete the category unconditionally, |
| 1077 | i.e. including all existing entries." | 1085 | i.e. including all existing entries." |
| 1078 | (interactive "P") | 1086 | (interactive "P") |
| 1079 | (if (and (null arg) | 1087 | (let* ((cat (todos-current-category)) |
| 1080 | ;; FIXME: what about done items? | 1088 | (not-done (car (todos-item-counts cat))) |
| 1081 | (not (eq (point-max) (point-min)))) | 1089 | (done (cdr (todos-item-counts cat))) |
| 1082 | (message "To delete a non-empty category, call the command with a prefix argument.") | 1090 | beg end) |
| 1083 | (let ((cat (nth todos-category-number todos-categories)) beg end) | 1091 | (if (and (null arg) |
| 1092 | (or (> not-done 0) (> done 0))) | ||
| 1093 | (message "To delete a non-empty category, type C-u D.") | ||
| 1084 | (when (y-or-n-p (concat "Permanently remove category \"" cat | 1094 | (when (y-or-n-p (concat "Permanently remove category \"" cat |
| 1085 | "\"" (and arg " and all its entries") "? ")) | 1095 | "\"" (and arg " and all its entries") "? ")) |
| 1086 | (let ((buffer-read-only)) | 1096 | (let ((buffer-read-only)) |
| @@ -1095,81 +1105,192 @@ i.e. including all existing entries." | |||
| 1095 | (remove-overlays beg end) | 1105 | (remove-overlays beg end) |
| 1096 | (kill-region beg end) | 1106 | (kill-region beg end) |
| 1097 | (setq todos-categories (delete cat todos-categories)) | 1107 | (setq todos-categories (delete cat todos-categories)) |
| 1108 | (setq todos-categories-alist | ||
| 1109 | (delete (assoc cat todos-categories-alist) todos-categories-alist)) | ||
| 1098 | (todos-category-select) | 1110 | (todos-category-select) |
| 1099 | (message "Deleted category %s" cat)))))) | 1111 | (message "Deleted category %s" cat)))))) |
| 1100 | 1112 | ||
| 1113 | (defun todos-raise-category (&optional lower) | ||
| 1114 | "Raise priority of category point is on in Categories buffer. | ||
| 1115 | With non-nil argument LOWER, lower the category's priority." | ||
| 1116 | (interactive) | ||
| 1117 | (let (num) | ||
| 1118 | (save-excursion | ||
| 1119 | (forward-line 0) | ||
| 1120 | (skip-chars-forward " ") | ||
| 1121 | (setq num (number-at-point))) | ||
| 1122 | (when (and num (if lower | ||
| 1123 | (< num (length todos-categories)) | ||
| 1124 | (> num 1))) | ||
| 1125 | (let* ((col (current-column)) | ||
| 1126 | (beg (progn (forward-line (if lower 0 -1)) (point))) | ||
| 1127 | (num1 (progn (skip-chars-forward " ") (1- (number-at-point)))) | ||
| 1128 | (num2 (1+ num1)) | ||
| 1129 | (end (progn (forward-line 2) (point))) | ||
| 1130 | (catvec (vconcat todos-categories)) | ||
| 1131 | (cat1 (aref catvec num1)) | ||
| 1132 | (cat2 (aref catvec num2)) | ||
| 1133 | (buffer-read-only)) | ||
| 1134 | (delete-region beg end) | ||
| 1135 | (setq num1 (1+ num1) | ||
| 1136 | num2 (1- num2)) | ||
| 1137 | (setq num num2) | ||
| 1138 | (todos-insert-category-name cat2) | ||
| 1139 | (setq num num1) | ||
| 1140 | (todos-insert-category-name cat1) | ||
| 1141 | (aset catvec num2 cat2) | ||
| 1142 | (aset catvec num1 cat1) | ||
| 1143 | (setq todos-categories (append catvec nil)) | ||
| 1144 | (forward-line (if lower -1 -2)) | ||
| 1145 | (forward-char col))))) | ||
| 1146 | |||
| 1147 | (defun todos-lower-category () | ||
| 1148 | "Lower priority of category point is on in Categories buffer." | ||
| 1149 | (interactive) | ||
| 1150 | (todos-raise-category t)) | ||
| 1151 | |||
| 1101 | ;;;###autoload | 1152 | ;;;###autoload |
| 1102 | (defun todos-insert-item (&optional arg here date-time) ; FIXME revise docstring | 1153 | (defun todos-insert-item (&optional arg date-type time diary here) |
| 1103 | "Insert new TODO list item. | 1154 | "Insert new TODO list item. |
| 1104 | 1155 | ||
| 1105 | With prefix argument ARG solicit the category, otherwise use the | 1156 | With prefix argument ARG solicit the category, otherwise use the |
| 1106 | current category. | 1157 | current category. |
| 1107 | 1158 | ||
| 1159 | Argument DATE-TYPE sets the form of the item's mandatory date | ||
| 1160 | string. With the value `date' this is the full date (whose | ||
| 1161 | format is set by `calendar-date-display-form', with year, month | ||
| 1162 | and day individually solicited (month with tab completion). With | ||
| 1163 | the value `dayname' a weekday name is used, solicited with tab | ||
| 1164 | completion. With the value `calendar' the full date string is | ||
| 1165 | used and set by selecting from the Calendar. With any other | ||
| 1166 | value (including none) the full current date is used. | ||
| 1167 | |||
| 1168 | Argument TIME determines the occurrence and value of the time | ||
| 1169 | string. With the value `omit' insert the item without a time | ||
| 1170 | string. With the value `ask' solicit a time string; this may be | ||
| 1171 | empty or else must match `date-time-regexp'. With any other | ||
| 1172 | value add or omit the current time in accordance with | ||
| 1173 | `todos-always-add-time-string'. | ||
| 1174 | |||
| 1175 | With non-nil argument DIARY mark item for inclusion in user's diary. If `todos-include-in-diary' is non-nil | ||
| 1176 | |||
| 1108 | With non-nil argument HERE insert the new item directly above the | 1177 | With non-nil argument HERE insert the new item directly above the |
| 1109 | item at point. If point is on an empty line, insert the new item | 1178 | item at point. If point is on an empty line, insert the new item |
| 1110 | there. | 1179 | there." |
| 1111 | |||
| 1112 | If the value of TIME is `omit', insert the item without a time | ||
| 1113 | string; with the value `ask', solicit a time string; with any | ||
| 1114 | other value, add or omit the current time in accordance with | ||
| 1115 | `todos-add-time-string'." | ||
| 1116 | (interactive "P") | 1180 | (interactive "P") |
| 1117 | (unless (or (todos-done-item-p) | 1181 | (unless (or (todos-done-item-p) |
| 1118 | (save-excursion (forward-line -1) (todos-done-item-p))) | 1182 | (save-excursion (forward-line -1) (todos-done-item-p))) |
| 1119 | (if (not (derived-mode-p 'todos-mode)) (todos-show)) | 1183 | (if (not (derived-mode-p 'todos-mode)) (todos-show)) |
| 1120 | (let* ((buffer-read-only) | 1184 | (let* ((buffer-read-only) |
| 1121 | (date-string (cond ;; ((eq date-time 'omit) "") | 1185 | (date-string (cond |
| 1122 | ((eq date-time 'ask) | 1186 | ((eq date-type 'ask-date) |
| 1123 | (read-from-minibuffer "Enter a date: ")) | 1187 | (todos-read-date)) |
| 1124 | ((eq date-time 'to-date) | 1188 | ((eq date-type 'ask-dayname) |
| 1189 | (todos-read-dayname)) | ||
| 1190 | ((eq date-type 'calendar) | ||
| 1191 | ;; FIXME: should only be executed from Calendar | ||
| 1125 | (with-current-buffer "*Calendar*" | 1192 | (with-current-buffer "*Calendar*" |
| 1126 | (calendar-date-string (calendar-cursor-to-date t) t t))) | 1193 | (calendar-date-string (calendar-cursor-to-date t) t t))) |
| 1127 | (t (calendar-date-string (calendar-current-date) t t)))) | 1194 | (t (calendar-date-string (calendar-current-date) t t)))) |
| 1128 | (time-string (if todos-add-time-string | 1195 | (time-string (cond ((eq time 'omit) nil) |
| 1129 | (cond ((eq date-time 'omit) "") | 1196 | ((eq time 'ask-time) |
| 1130 | ((eq date-time 'ask) | 1197 | (todos-read-time)) |
| 1131 | (read-from-minibuffer "Enter a clock time: ")) | 1198 | (todos-always-add-time-string |
| 1132 | (t (substring (current-time-string) 11 16))) | 1199 | (substring (current-time-string) 11 16)))) |
| 1133 | "")) | 1200 | (new-item (concat (unless (or diary todos-include-in-diary) "[") ;FIXME |
| 1134 | (new-item (concat (unless todos-include-in-diary "[") | 1201 | date-string (when time-string (concat " " time-string)) |
| 1135 | date-string (unless (string= time-string "") | 1202 | ;; FIXME |
| 1136 | (concat " " time-string)) | 1203 | (unless (or diary todos-include-in-diary) "]") " " |
| 1137 | (unless todos-include-in-diary "]") " " | ||
| 1138 | (read-from-minibuffer "New TODO entry: "))) | 1204 | (read-from-minibuffer "New TODO entry: "))) |
| 1139 | (current-category (nth todos-category-number todos-categories)) | 1205 | (cat (if arg (todos-read-category) (todos-current-category)))) |
| 1140 | (category (if arg (todos-completing-read) current-category))) | 1206 | ;; indent newlines inserted by C-q C-j if nonspace char follows |
| 1141 | (if here | 1207 | (setq new-item (replace-regexp-in-string |
| 1142 | (todos-insert-with-overlays new-item) | 1208 | "\\(\n\\)[^[:blank:]]" |
| 1143 | (todos-add-item-non-interactively new-item category))))) | 1209 | (concat "\n" (make-string todos-indent-to-here 32)) new-item |
| 1144 | 1210 | nil nil 1)) | |
| 1145 | (defun todos-insert-item-here (&optional date-time) | 1211 | ;; (if here |
| 1146 | "" ;FIXME add docstring | 1212 | ;; (todos-insert-with-overlays new-item) |
| 1213 | ;; (todos-add-item-non-interactively new-item cat)) | ||
| 1214 | (unless here (todos-set-item-priority new-item cat)) | ||
| 1215 | (todos-insert-with-overlays new-item) | ||
| 1216 | (todos-item-counts cat 'insert)))) | ||
| 1217 | |||
| 1218 | ;; FIXME: make insertion options customizable per category | ||
| 1219 | ;; date-type: d n (c) - time - diary - here | ||
| 1220 | ;; idd inn itt iyy ih | ||
| 1221 | ;; idtt idyy idh intt inyy inh ityy iyh | ||
| 1222 | ;; idtyy idyh intyy inyh ityh | ||
| 1223 | ;; idtyh intyh | ||
| 1224 | ;; idth inth | ||
| 1225 | |||
| 1226 | ;; todos-insert-item | ||
| 1227 | ;; todos-insert-item-ask-date | ||
| 1228 | ;; todos-insert-item-ask-date-time | ||
| 1229 | ;; todos-insert-item-ask-dayname | ||
| 1230 | ;; todos-insert-item-ask-dayname-time | ||
| 1231 | ;; todos-insert-item-ask-time | ||
| 1232 | ;; todos-insert-item-for-diary | ||
| 1233 | ;; todos-insert-item-for-diary-ask-date | ||
| 1234 | ;; todos-insert-item-for-diary-ask-date-time | ||
| 1235 | ;; todos-insert-item-for-diary-ask-dayname | ||
| 1236 | ;; todos-insert-item-for-diary-ask-dayname-time | ||
| 1237 | ;; todos-insert-item-for-diary-ask-time | ||
| 1238 | ;; todos-insert-item-here | ||
| 1239 | ;; todos-insert-item-here-ask-date | ||
| 1240 | ;; todos-insert-item-here-ask-date-time | ||
| 1241 | ;; todos-insert-item-here-ask-dayname | ||
| 1242 | ;; todos-insert-item-here-ask-dayname-time | ||
| 1243 | ;; todos-insert-item-here-ask-time | ||
| 1244 | ;; todos-insert-item-here-ask-time-diary | ||
| 1245 | ;; todos-insert-item-here-for-diary | ||
| 1246 | ;; todos-insert-item-here-for-diary-ask-date-time | ||
| 1247 | ;; todos-insert-item-here-for-diary-ask-time | ||
| 1248 | ;; todos-insert-item-here-for-diary-ask-dayname-time | ||
| 1249 | |||
| 1250 | (defun todos-insert-item-here () | ||
| 1251 | "" | ||
| 1147 | (interactive) | 1252 | (interactive) |
| 1148 | (todos-insert-item nil t date-time)) | 1253 | (todos-insert-item nil nil nil t)) |
| 1149 | 1254 | ||
| 1150 | (defun todos-insert-item-no-time (&optional here) | 1255 | (defun todos-insert-item-here-ask-date-time () |
| 1151 | "" ;FIXME add docstring | 1256 | "" |
| 1152 | (interactive) | 1257 | (interactive) |
| 1153 | (todos-insert-item nil here 'omit)) | 1258 | (todos-insert-item nil 'ask-date 'ask-time t)) |
| 1259 | |||
| 1260 | ;; (defun todos-insert-item-no-time () | ||
| 1261 | ;; "" | ||
| 1262 | ;; (interactive) | ||
| 1263 | ;; (todos-insert-item nil nil 'omit t)) | ||
| 1264 | |||
| 1265 | (defun todos-insert-item-ask-date-time (&optional arg) | ||
| 1266 | "" | ||
| 1267 | (interactive "P") | ||
| 1268 | (todos-insert-item arg 'ask-date 'ask-time)) | ||
| 1154 | 1269 | ||
| 1155 | (defun todos-insert-item-ask-date (&optional here) | 1270 | (defun todos-insert-item-ask-dayname-time (&optional arg) |
| 1156 | "" ;FIXME add docstring | 1271 | "" |
| 1157 | (interactive) | 1272 | (interactive) |
| 1158 | (todos-insert-item nil here 'ask)) | 1273 | (todos-insert-item arg 'ask-dayname 'ask-time)) |
| 1274 | |||
| 1275 | (defun todos-insert-item-for-diary (&optional arg) | ||
| 1276 | "" | ||
| 1277 | (interactive "P") | ||
| 1278 | (let ((todos-include-in-diary t)) | ||
| 1279 | (todos-insert-item arg))) | ||
| 1159 | 1280 | ||
| 1160 | (defun todos-insert-item-for-diary (&optional arg here date-time) | 1281 | (defun todos-insert-item-for-diary-ask-date-time (&optional arg) |
| 1161 | "" ;FIXME | 1282 | "" |
| 1162 | (interactive "P") | 1283 | (interactive "P") |
| 1163 | (let ((todos-include-in-diary t)) | 1284 | (let ((todos-include-in-diary t)) |
| 1164 | (todos-insert-item arg here date-time))) | 1285 | (todos-insert-item arg 'ask-dayname 'ask-time))) |
| 1165 | 1286 | ||
| 1166 | ;; FIXME: autoload when key-binding is defined in calendar.el | 1287 | ;; FIXME: autoload when key-binding is defined in calendar.el |
| 1167 | (defun todos-insert-item-from-calendar () | 1288 | (defun todos-insert-item-from-calendar () |
| 1168 | "" ;FIXME | 1289 | "" |
| 1169 | (interactive) | 1290 | (interactive) |
| 1170 | (pop-to-buffer (file-name-nondirectory todos-file-do)) | 1291 | (pop-to-buffer (file-name-nondirectory todos-file-do)) |
| 1171 | (todos-show) | 1292 | (todos-show) |
| 1172 | (todos-insert-item t nil 'to-date)) | 1293 | (todos-insert-item t 'calendar)) |
| 1173 | 1294 | ||
| 1174 | ;; FIXME: calendar is loaded before todos | 1295 | ;; FIXME: calendar is loaded before todos |
| 1175 | ;; (add-hook 'calendar-load-hook | 1296 | ;; (add-hook 'calendar-load-hook |
| @@ -1190,11 +1311,12 @@ other value, add or omit the current time in accordance with | |||
| 1190 | ;; not if last item was deleted | 1311 | ;; not if last item was deleted |
| 1191 | (< (point-min) (point-max))) | 1312 | (< (point-min) (point-max))) |
| 1192 | (todos-backward-item)) | 1313 | (todos-backward-item)) |
| 1314 | (todos-item-counts (todos-current-category) 'delete) | ||
| 1193 | ;; FIXME: is todos-prefix-overlays part of if-sexp, and is it needed | 1315 | ;; FIXME: is todos-prefix-overlays part of if-sexp, and is it needed |
| 1194 | ;; at all? | 1316 | ;; at all? |
| 1195 | (if todos-number-prefix | 1317 | ;; (if todos-number-prefix |
| 1196 | (todos-update-numbered-prefix) | 1318 | ;; (todos-update-numbered-prefix) |
| 1197 | (todos-prefix-overlays)))) | 1319 | (todos-prefix-overlays)));) |
| 1198 | (error "No TODO list entry to delete"))) | 1320 | (error "No TODO list entry to delete"))) |
| 1199 | 1321 | ||
| 1200 | (defun todos-edit-item () | 1322 | (defun todos-edit-item () |
| @@ -1208,6 +1330,11 @@ other value, add or omit the current time in accordance with | |||
| 1208 | (let ((new (read-from-minibuffer "Edit: " item))) | 1330 | (let ((new (read-from-minibuffer "Edit: " item))) |
| 1209 | (while (not (string-match (concat "^\\[?" todos-date-pattern) new)) | 1331 | (while (not (string-match (concat "^\\[?" todos-date-pattern) new)) |
| 1210 | (setq new (read-from-minibuffer "Item must start with a date: " new))) | 1332 | (setq new (read-from-minibuffer "Item must start with a date: " new))) |
| 1333 | ;; indent newlines inserted by C-q C-j if nonspace char follows | ||
| 1334 | (setq new (replace-regexp-in-string | ||
| 1335 | "\\(\n\\)[^[:blank:]]" | ||
| 1336 | (concat "\n" (make-string todos-indent-to-here 32)) new | ||
| 1337 | nil nil 1)) | ||
| 1211 | ;; If user moved point during editing, make sure it moves back. | 1338 | ;; If user moved point during editing, make sure it moves back. |
| 1212 | (goto-char opoint) | 1339 | (goto-char opoint) |
| 1213 | (todos-remove-item) | 1340 | (todos-remove-item) |
| @@ -1227,31 +1354,22 @@ other value, add or omit the current time in accordance with | |||
| 1227 | (narrow-to-region (todos-item-start) (todos-item-end)))) | 1354 | (narrow-to-region (todos-item-start) (todos-item-end)))) |
| 1228 | 1355 | ||
| 1229 | (defun todos-edit-quit () | 1356 | (defun todos-edit-quit () |
| 1230 | "" ;FIXME | 1357 | "" |
| 1231 | (interactive) | 1358 | (interactive) |
| 1232 | (save-excursion (todos-category-select))) | 1359 | (save-excursion (todos-category-select))) |
| 1233 | 1360 | ||
| 1234 | ;; FIXME | 1361 | ;; FIXME: complete |
| 1235 | (defun todos-change-date (&optional event) | 1362 | (defun todos-edit-item-header () |
| 1236 | "" ;FIXME | 1363 | "" |
| 1237 | (interactive) | 1364 | (interactive) |
| 1238 | (let (dmarker | 1365 | (todos-item-start) |
| 1239 | calendar-view-diary-initially-flag | 1366 | (re-search-forward (concat "^\\[?\\(?1:" todos-date-pattern |
| 1240 | new-date) | 1367 | "\\) \\(?2:" diary-time-regexp "\\)") |
| 1241 | (save-excursion | 1368 | (line-end-position) t) |
| 1242 | (todos-item-start) | 1369 | ;; ask date or dayname |
| 1243 | (setq dmarker (point-marker))) | 1370 | (replace-match new-date nil nil nil 1) |
| 1244 | (calendar) | 1371 | ;; ask time |
| 1245 | (message "Put the cursor on the desired date in the Calendar and press `q'") | 1372 | (replace-match new-date nil nil nil 2)) |
| 1246 | (setq new-date | ||
| 1247 | (calendar-date-string (calendar-cursor-to-date t) t t)) | ||
| 1248 | ;; (pop-to-buffer (file-name-nondirectory todos-file-do)) | ||
| 1249 | ;; (todos-show) | ||
| 1250 | (when (eq last-command 'calendar-exit) | ||
| 1251 | (goto-char (marker-position dmarker)) | ||
| 1252 | (re-search-forward (concat "^\\[?\\(" todos-date-pattern "\\)\\]?") | ||
| 1253 | (line-end-position) t) | ||
| 1254 | (replace-match new-date nil nil nil 1)))) | ||
| 1255 | 1373 | ||
| 1256 | (defun todos-raise-item () | 1374 | (defun todos-raise-item () |
| 1257 | "Raise priority of current entry." | 1375 | "Raise priority of current entry." |
| @@ -1271,48 +1389,28 @@ other value, add or omit the current time in accordance with | |||
| 1271 | (interactive) | 1389 | (interactive) |
| 1272 | (unless (or (todos-done-item-p) | 1390 | (unless (or (todos-done-item-p) |
| 1273 | (looking-at "^$")) ; between done and not done items | 1391 | (looking-at "^$")) ; between done and not done items |
| 1274 | (let ((buffer-read-only) | 1392 | (let* ((buffer-read-only) |
| 1275 | (done (save-excursion | 1393 | ;; (end (save-excursion (todos-forward-item) (point))) |
| 1276 | (if (re-search-forward (concat "\n\n\\\[" | 1394 | ;; (done (save-excursion |
| 1277 | (regexp-quote todos-done-string)) | 1395 | ;; (if (re-search-forward (concat "\n\n\\\[" |
| 1278 | nil t) | 1396 | ;; (regexp-quote todos-done-string)) |
| 1279 | (match-beginning 0) | 1397 | ;; nil t) |
| 1280 | (point-max))))) | 1398 | ;; (match-beginning 0) |
| 1281 | (if (> (count-lines (point) done) 1) | 1399 | ;; (point-max)))) |
| 1400 | ) | ||
| 1401 | ;; (if (> (count-lines (point) done) 1) | ||
| 1402 | (if (save-excursion | ||
| 1403 | ;; can only lower non-final unfinished item | ||
| 1404 | (todos-forward-item) | ||
| 1405 | (and (looking-at todos-item-start) | ||
| 1406 | (not (todos-done-item-p)))) | ||
| 1282 | ;; Assume there is a final newline | 1407 | ;; Assume there is a final newline |
| 1283 | (let ((item (todos-item-string)) | 1408 | (let ((item (todos-item-string))) |
| 1284 | opoint) | ||
| 1285 | (todos-remove-item) | 1409 | (todos-remove-item) |
| 1286 | (todos-forward-item) | 1410 | (todos-forward-item) |
| 1411 | (when (todos-done-item-p) (forward-line -1)) | ||
| 1287 | (todos-insert-with-overlays item)) | 1412 | (todos-insert-with-overlays item)) |
| 1288 | (error "No TODO list entry to lower"))))) | 1413 | (error "No TODO list entry to lower"))))) ;FIXME: better message |
| 1289 | |||
| 1290 | ;; FIXME: moves last not done item when point on empty line below it | ||
| 1291 | ;; (defun todos-move-item () | ||
| 1292 | ;; "Move the current todo item to another, interactively named, category. | ||
| 1293 | |||
| 1294 | ;; If the named category is not one of the current todo categories, then | ||
| 1295 | ;; it is created and the item becomes the first entry in that category." | ||
| 1296 | ;; (interactive) | ||
| 1297 | ;; (unless (or (todos-done-item-p) | ||
| 1298 | ;; (looking-at "^$")) ; between done and not done items | ||
| 1299 | ;; (let ((item (todos-item-string)) | ||
| 1300 | ;; (category (todos-completing-read)) | ||
| 1301 | ;; orig moved) | ||
| 1302 | ;; (setq (save-excursion (todos-item-start))) | ||
| 1303 | ;; (todos-remove-item) | ||
| 1304 | ;; ;; numbered prefix isn't cached (see todos-remove-item) so have to update | ||
| 1305 | ;; (if todos-number-prefix (todos-update-numbered-prefix)) | ||
| 1306 | ;; (setq chgr (prepare-change-group)) | ||
| 1307 | ;; ;; FIXME | ||
| 1308 | ;; (unwind-protect | ||
| 1309 | ;; (progn | ||
| 1310 | ;; (activate-change-group chgr) | ||
| 1311 | ;; (todos-add-item-non-interactively item category) | ||
| 1312 | ;; (setq moved t)) | ||
| 1313 | ;; (if moved | ||
| 1314 | ;; (accept-change-group chgr) | ||
| 1315 | ;; (cancel-change-group chgr)))))) | ||
| 1316 | 1414 | ||
| 1317 | (defun todos-move-item () | 1415 | (defun todos-move-item () |
| 1318 | "Move the current todo item to another, interactively named, category. | 1416 | "Move the current todo item to another, interactively named, category. |
| @@ -1324,57 +1422,35 @@ it is created and the item becomes the first entry in that category." | |||
| 1324 | (looking-at "^$")) ; between done and not done items | 1422 | (looking-at "^$")) ; between done and not done items |
| 1325 | (let ((buffer-read-only) | 1423 | (let ((buffer-read-only) |
| 1326 | (oldnum todos-category-number) | 1424 | (oldnum todos-category-number) |
| 1327 | (oldcat (nth todos-category-number todos-categories)) | 1425 | (oldcat (todos-current-category)) |
| 1328 | (item (todos-item-string)) | 1426 | (item (todos-item-string)) |
| 1329 | (newcat (todos-completing-read)) | 1427 | (newcat (todos-read-category)) |
| 1330 | (opoint (point)) | 1428 | (opoint (point)) |
| 1331 | (orig-mrk (save-excursion (todos-item-start) (point-marker))) | 1429 | (orig-mrk (progn (todos-item-start) (point-marker))) |
| 1332 | moved) | 1430 | moved) |
| 1333 | (todos-remove-item) | 1431 | (todos-remove-item) |
| 1334 | ;; numbered prefix isn't cached (see todos-remove-item) so have to update | 1432 | ;; numbered prefix isn't cached (see todos-remove-item) so have to update |
| 1335 | (if todos-number-prefix (todos-update-numbered-prefix)) | 1433 | ;; (if todos-number-prefix (todos-update-numbered-prefix)) |
| 1336 | (unwind-protect | 1434 | (unwind-protect |
| 1337 | (progn | 1435 | (progn |
| 1338 | (todos-add-item-non-interactively item newcat) | 1436 | ;; (todos-add-item-non-interactively item newcat) |
| 1339 | (setq moved t)) | 1437 | (todos-set-item-priority item newcat) |
| 1438 | (todos-insert-with-overlays item) | ||
| 1439 | (setq moved t) | ||
| 1440 | (todos-item-counts oldcat 'delete) | ||
| 1441 | (todos-item-counts newcat 'insert)) | ||
| 1340 | (unless moved | 1442 | (unless moved |
| 1341 | (widen) | 1443 | (widen) |
| 1342 | (goto-char orig-mrk) | 1444 | (goto-char orig-mrk) |
| 1343 | (todos-insert-with-overlays item) | 1445 | (todos-insert-with-overlays item) |
| 1344 | (setq todos-category-number oldnum) | 1446 | (setq todos-category-number oldnum) |
| 1447 | ;; (todos-item-counts oldcat 'move-failed) | ||
| 1448 | ;; (todos-item-counts newcat 'move-failed) | ||
| 1345 | (todos-category-select) | 1449 | (todos-category-select) |
| 1346 | ;; FIXME: does this work? | 1450 | ;; FIXME: does this work? |
| 1347 | (goto-char opoint)) | 1451 | (goto-char opoint)) |
| 1348 | (set-marker orig-mrk nil))))) | 1452 | (set-marker orig-mrk nil))))) |
| 1349 | 1453 | ||
| 1350 | ;; (defun todos-file-item (&optional comment) | ||
| 1351 | ;; "File the current TODO list entry away, annotated with an optional COMMENT." | ||
| 1352 | ;; (interactive "sComment: ") | ||
| 1353 | ;; (or (> (count-lines (point-min) (point-max)) 0) | ||
| 1354 | ;; (error "No TODO list entry to file away")) | ||
| 1355 | ;; (let ((time-stamp-format todos-time-string-format)) | ||
| 1356 | ;; (when (and comment (> (length comment) 0)) | ||
| 1357 | ;; (goto-char (todos-item-end)) | ||
| 1358 | ;; (insert | ||
| 1359 | ;; (if (save-excursion (beginning-of-line) | ||
| 1360 | ;; (looking-at (regexp-quote todos-prefix))) | ||
| 1361 | ;; " " | ||
| 1362 | ;; "\n\t") | ||
| 1363 | ;; "(" comment ")")) | ||
| 1364 | ;; (goto-char (todos-item-end)) | ||
| 1365 | ;; (insert " [" (nth todos-category-number todos-categories) "]") | ||
| 1366 | ;; (goto-char (todos-item-start)) | ||
| 1367 | ;; (let ((temp-point (point))) | ||
| 1368 | ;; (if (looking-at (regexp-quote todos-prefix)) | ||
| 1369 | ;; (replace-match (time-stamp-string)) | ||
| 1370 | ;; ;; Standard prefix -> timestamp | ||
| 1371 | ;; ;; Else prefix non-standard item start with timestamp | ||
| 1372 | ;; (insert (time-stamp-string))) | ||
| 1373 | ;; (append-to-file temp-point (1+ (todos-item-end)) todos-file-done) | ||
| 1374 | ;; (delete-region temp-point (1+ (todos-item-end)))) | ||
| 1375 | ;; (todos-backward-item) | ||
| 1376 | ;; (message "")) | ||
| 1377 | |||
| 1378 | (defun todos-item-done () | 1454 | (defun todos-item-done () |
| 1379 | "Mark current item as done and move it to category's done section." | 1455 | "Mark current item as done and move it to category's done section." |
| 1380 | (interactive) | 1456 | (interactive) |
| @@ -1383,7 +1459,7 @@ it is created and the item becomes the first entry in that category." | |||
| 1383 | (let* ((buffer-read-only) | 1459 | (let* ((buffer-read-only) |
| 1384 | (item (todos-item-string)) | 1460 | (item (todos-item-string)) |
| 1385 | (date-string (calendar-date-string (calendar-current-date) t t)) | 1461 | (date-string (calendar-date-string (calendar-current-date) t t)) |
| 1386 | (time-string (if todos-add-time-string | 1462 | (time-string (if todos-always-add-time-string ;FIXME: delete condition |
| 1387 | (concat " " (substring (current-time-string) 11 16)) | 1463 | (concat " " (substring (current-time-string) 11 16)) |
| 1388 | "")) | 1464 | "")) |
| 1389 | (done-item (concat "[" todos-done-string date-string time-string "] " item)) | 1465 | (done-item (concat "[" todos-done-string date-string time-string "] " item)) |
| @@ -1407,13 +1483,14 @@ it is created and the item becomes the first entry in that category." | |||
| 1407 | (goto-char next-cat) | 1483 | (goto-char next-cat) |
| 1408 | (newline)) | 1484 | (newline)) |
| 1409 | (todos-insert-with-overlays done-item))) | 1485 | (todos-insert-with-overlays done-item))) |
| 1486 | (todos-item-counts (todos-current-category) 'done) | ||
| 1410 | (todos-show))) | 1487 | (todos-show))) |
| 1411 | 1488 | ||
| 1412 | (defun todos-archive-done-items () | 1489 | (defun todos-archive-done-items () |
| 1413 | "Archive the done items in the current category." | 1490 | "Archive the done items in the current category." |
| 1414 | (interactive) | 1491 | (interactive) |
| 1415 | (let ((archive (find-file-noselect todos-archive-file t)) | 1492 | (let ((archive (find-file-noselect todos-archive-file t)) |
| 1416 | (cat (nth todos-category-number todos-categories)) | 1493 | (cat (todos-current-category)) |
| 1417 | beg end) | 1494 | beg end) |
| 1418 | (save-excursion | 1495 | (save-excursion |
| 1419 | (save-restriction | 1496 | (save-restriction |
| @@ -1423,11 +1500,12 @@ it is created and the item becomes the first entry in that category." | |||
| 1423 | (re-search-backward (concat "^" (regexp-quote todos-category-beg) | 1500 | (re-search-backward (concat "^" (regexp-quote todos-category-beg) |
| 1424 | (regexp-quote cat)) | 1501 | (regexp-quote cat)) |
| 1425 | nil t) | 1502 | nil t) |
| 1426 | (if (not (re-search-forward (concat "\\[" (regexp-quote todos-done-string)) | 1503 | (if (not (re-search-forward (concat "^\\[" (regexp-quote todos-done-string)) |
| 1427 | nil t)) | 1504 | nil t)) |
| 1428 | (error "No done items in this category") | 1505 | (error "No done items in this category") |
| 1429 | (setq beg (match-beginning 0)) | 1506 | (setq beg (match-beginning 0)) |
| 1430 | (setq done (buffer-substring beg end)) | 1507 | (setq done (buffer-substring beg end)) |
| 1508 | ;; FIXME: update archive alist | ||
| 1431 | (with-current-buffer archive | 1509 | (with-current-buffer archive |
| 1432 | (goto-char (point-min)) | 1510 | (goto-char (point-min)) |
| 1433 | (if (re-search-forward (regexp-quote (concat "^" todos-category-beg cat)) | 1511 | (if (re-search-forward (regexp-quote (concat "^" todos-category-beg cat)) |
| @@ -1437,25 +1515,43 @@ it is created and the item becomes the first entry in that category." | |||
| 1437 | (insert done)) | 1515 | (insert done)) |
| 1438 | (delete-region beg end) | 1516 | (delete-region beg end) |
| 1439 | (remove-overlays beg end) | 1517 | (remove-overlays beg end) |
| 1440 | (kill-line -1))))) | 1518 | (kill-line -1) |
| 1519 | (todos-item-counts cat 'archive))))) | ||
| 1441 | (message "Done items archived.")) | 1520 | (message "Done items archived.")) |
| 1442 | 1521 | ||
| 1443 | ;; FIXME: undone item leaves item number overlay behind | ||
| 1444 | (defun todos-item-undo () | 1522 | (defun todos-item-undo () |
| 1445 | "" ;FIXME | 1523 | "" |
| 1446 | (interactive) | 1524 | (interactive) |
| 1447 | (when (todos-done-item-p) | 1525 | (when (todos-done-item-p) |
| 1448 | (let* ((buffer-read-only) | 1526 | (let* ((buffer-read-only) |
| 1449 | (cat (nth todos-category-number todos-categories)) | 1527 | (cat (todos-current-category)) |
| 1450 | (start (progn | 1528 | (done-item (todos-item-string)) |
| 1451 | (todos-item-start) | 1529 | (opoint (point)) |
| 1452 | (search-forward "] "))) ; end of done date string | 1530 | (orig-mrk (progn (todos-item-start) (point-marker))) |
| 1453 | (item (buffer-substring start (todos-item-end)))) | 1531 | (start (search-forward "] ")) ; end of done date string |
| 1532 | (item (buffer-substring start (todos-item-end))) | ||
| 1533 | undone) | ||
| 1454 | (todos-remove-item) | 1534 | (todos-remove-item) |
| 1455 | (todos-add-item-non-interactively item cat)))) | 1535 | ;; (if todos-number-prefix (todos-update-numbered-prefix)) |
| 1536 | (unwind-protect | ||
| 1537 | (progn | ||
| 1538 | ;; (todos-add-item-non-interactively item cat) | ||
| 1539 | (todos-set-item-priority item cat) | ||
| 1540 | (todos-insert-with-overlays item) | ||
| 1541 | (setq undone t) | ||
| 1542 | (todos-item-counts cat 'undo)) | ||
| 1543 | (unless undone | ||
| 1544 | (widen) | ||
| 1545 | (goto-char orig-mrk) | ||
| 1546 | (todos-insert-with-overlays done-item) | ||
| 1547 | ;; (todos-item-counts cat 'done) | ||
| 1548 | (let ((todos-show-with-done t)) | ||
| 1549 | (todos-category-select) | ||
| 1550 | (goto-char opoint))) | ||
| 1551 | (set-marker orig-mrk nil))))) | ||
| 1456 | 1552 | ||
| 1457 | (defun todos-toggle-item-diary-inclusion () | 1553 | (defun todos-toggle-item-diary-inclusion () |
| 1458 | "" ;FIXME add docstring | 1554 | "" |
| 1459 | (interactive) | 1555 | (interactive) |
| 1460 | (save-excursion | 1556 | (save-excursion |
| 1461 | (let* ((buffer-read-only) | 1557 | (let* ((buffer-read-only) |
| @@ -1475,7 +1571,7 @@ it is created and the item becomes the first entry in that category." | |||
| 1475 | (insert "]")))))) ; FIXME use todos-exclusion-end | 1571 | (insert "]")))))) ; FIXME use todos-exclusion-end |
| 1476 | 1572 | ||
| 1477 | (defun todos-toggle-diary-inclusion (arg) | 1573 | (defun todos-toggle-diary-inclusion (arg) |
| 1478 | "" ;FIXME add docstring | 1574 | "" |
| 1479 | (interactive "p") | 1575 | (interactive "p") |
| 1480 | (save-excursion | 1576 | (save-excursion |
| 1481 | (save-restriction | 1577 | (save-restriction |
| @@ -1527,64 +1623,37 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1527 | 1623 | ||
| 1528 | ;;; Internal functions | 1624 | ;;; Internal functions |
| 1529 | 1625 | ||
| 1530 | ;; "Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct\\|Nov\\|Dec") | ||
| 1531 | ;; (regexp-opt (list "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30" "31"))) | ||
| 1532 | |||
| 1533 | ;; FIXME: use diary-date-forms instead? | ||
| 1534 | ;; (defun todos-date-string () | ||
| 1535 | ;; "Return a regexp matching a diary date string." | ||
| 1536 | ;; (let ((month (regexp-opt (list "Jan" "Feb" "Mar" "Apr" "May" "Jun" | ||
| 1537 | ;; "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))) | ||
| 1538 | ;; (day "[0-3]?[0-9]") | ||
| 1539 | ;; (year "[0-9]\\{4\\}")) | ||
| 1540 | ;; (concat month " " day ", " year))) | ||
| 1541 | |||
| 1542 | ;; FIXME: use diary-time-regexp | ||
| 1543 | ;; (defun todos-time-string () | ||
| 1544 | ;; "Return a regexp matching a diary time string." | ||
| 1545 | ;; "[0-9]?[0-9][:.][0-9]\\{2\\}") | ||
| 1546 | |||
| 1547 | (defvar todos-date-nodayname-pattern | ||
| 1548 | (let ((dayname) | ||
| 1549 | (monthname (format "\\(%s\\|\\*\\)" | ||
| 1550 | (diary-name-pattern calendar-month-name-array | ||
| 1551 | calendar-month-abbrev-array t))) | ||
| 1552 | (month "\\([0-9]+\\|\\*\\)") | ||
| 1553 | (day "\\([0-9]+\\|\\*\\)") | ||
| 1554 | (year "-?\\([0-9]+\\|\\*\\)")) | ||
| 1555 | (mapconcat 'eval calendar-date-display-form "")) | ||
| 1556 | "Regular expression matching a Todos date header without day name.") | ||
| 1557 | |||
| 1558 | ;; (defvar todos-dayname-pattern | ||
| 1559 | ;; (diary-name-pattern calendar-day-name-array nil t) | ||
| 1560 | ;; "Regular expression matching a day name in a Todos date header.") | ||
| 1561 | |||
| 1562 | (defvar todos-dayname-date-pattern | ||
| 1563 | (let ((dayname (diary-name-pattern calendar-day-name-array nil t))) | ||
| 1564 | (concat dayname "\\(?:, " todos-date-nodayname-pattern "\\)?")) | ||
| 1565 | "Regular expression matching a Todos date header with day name.") | ||
| 1566 | |||
| 1567 | (defvar todos-date-pattern | 1626 | (defvar todos-date-pattern |
| 1568 | (concat "\\(?:" todos-date-nodayname-pattern "\\)\\|" | 1627 | (let ((dayname (diary-name-pattern calendar-day-name-array nil t))) |
| 1569 | "\\(?:" todos-date-dayname-pattern "\\)") | 1628 | (concat "\\(" dayname "\\|" |
| 1629 | (let ((dayname) | ||
| 1630 | (monthname (format "\\(%s\\|\\*\\)" | ||
| 1631 | (diary-name-pattern calendar-month-name-array | ||
| 1632 | calendar-month-abbrev-array | ||
| 1633 | t))) | ||
| 1634 | (month "\\([0-9]+\\|\\*\\)") | ||
| 1635 | (day "\\([0-9]+\\|\\*\\)") | ||
| 1636 | (year "-?\\([0-9]+\\|\\*\\)")) | ||
| 1637 | (mapconcat 'eval calendar-date-display-form "")) | ||
| 1638 | "\\)")) | ||
| 1570 | "Regular expression matching a Todos date header.") | 1639 | "Regular expression matching a Todos date header.") |
| 1571 | 1640 | ||
| 1572 | (defun todos-date-string-match (lim) | 1641 | (defun todos-date-string-match (lim) |
| 1573 | "Find Todos date strings for font-locking." | 1642 | "Find Todos date strings within LIM for font-locking." |
| 1574 | (re-search-forward (concat "^\\[?\\(" todos-date-pattern "\\)") lim t)) | 1643 | (re-search-forward (concat "^\\[?" todos-date-pattern) lim t)) |
| 1575 | 1644 | ||
| 1576 | (defun todos-time-string-match (lim) | 1645 | (defun todos-time-string-match (lim) |
| 1577 | "Find Todos time strings for font-locking." | 1646 | "Find Todos time strings within LIM for font-locking." |
| 1578 | (re-search-forward (concat "^\\[?\\(?:" todos-date-pattern "\\)" | 1647 | (re-search-forward (concat "^\\[?" todos-date-pattern |
| 1579 | " \\(?1:" diary-time-regexp "\\)") lim t)) | 1648 | " \\(?1:" diary-time-regexp "\\)") lim t)) |
| 1580 | 1649 | ||
| 1581 | (defun todos-done-string-match (lim) | 1650 | (defun todos-done-string-match (lim) |
| 1582 | "Find Todos done headers for font-locking." | 1651 | "Find Todos done headers within LIM for font-locking." |
| 1583 | (re-search-forward (concat "^\\[" (regexp-quote todos-done-string) "[^][]+]") | 1652 | (re-search-forward (concat "^\\[" (regexp-quote todos-done-string) "[^][]+]") |
| 1584 | lim t)) | 1653 | lim t)) |
| 1585 | 1654 | ||
| 1586 | (defun todos-category-string-match (lim) | 1655 | (defun todos-category-string-match (lim) |
| 1587 | "Find Todos category headers for font-locking." | 1656 | "Find Todos category headers within LIM for font-locking." |
| 1588 | (re-search-forward (concat "^" (regexp-quote todos-category-beg) ".*$") | 1657 | (re-search-forward (concat "^" (regexp-quote todos-category-beg) ".*$") |
| 1589 | lim t)) | 1658 | lim t)) |
| 1590 | 1659 | ||
| @@ -1606,14 +1675,19 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1606 | (message "This Todos file is well-formatted.")) | 1675 | (message "This Todos file is well-formatted.")) |
| 1607 | 1676 | ||
| 1608 | (defun todos-wrap-and-indent () | 1677 | (defun todos-wrap-and-indent () |
| 1609 | "" ;FIXME | 1678 | "" |
| 1610 | (make-local-variable 'word-wrap) | 1679 | (make-local-variable 'word-wrap) |
| 1611 | (setq word-wrap t) | 1680 | (setq word-wrap t) |
| 1612 | (make-local-variable 'wrap-prefix) | 1681 | (make-local-variable 'wrap-prefix) |
| 1613 | (setq wrap-prefix (make-string (+ 5 (length todos-prefix)) 32)) | 1682 | ;; (setq wrap-prefix (make-string (+ 5 (length todos-prefix)) 32)) |
| 1683 | (setq wrap-prefix (make-string todos-indent-to-here 32)) | ||
| 1614 | (unless (member '(continuation) fringe-indicator-alist) | 1684 | (unless (member '(continuation) fringe-indicator-alist) |
| 1615 | (push '(continuation) fringe-indicator-alist))) | 1685 | (push '(continuation) fringe-indicator-alist))) |
| 1616 | 1686 | ||
| 1687 | (defun todos-indent () | ||
| 1688 | "" | ||
| 1689 | (indent-to todos-indent-to-here todos-indent-to-here)) | ||
| 1690 | |||
| 1617 | (defun todos-reset-prefix (symbol value) | 1691 | (defun todos-reset-prefix (symbol value) |
| 1618 | "Set SYMBOL's value to VALUE, and ." ; FIXME | 1692 | "Set SYMBOL's value to VALUE, and ." ; FIXME |
| 1619 | (let ((oldvalue (symbol-value symbol))) | 1693 | (let ((oldvalue (symbol-value symbol))) |
| @@ -1628,77 +1702,91 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1628 | (remove-overlays (point) (point)); 'before-string prefix) | 1702 | (remove-overlays (point) (point)); 'before-string prefix) |
| 1629 | (forward-line))) | 1703 | (forward-line))) |
| 1630 | ;; activate the prefix setting (save-restriction does not help) | 1704 | ;; activate the prefix setting (save-restriction does not help) |
| 1631 | (todos-show))))) | 1705 | ;; (todos-show) |
| 1632 | 1706 | (todos-category-select) | |
| 1633 | ;; FIXME: rename and/or rewrite | 1707 | )))) |
| 1634 | (defun todos-update-numbered-prefix () | 1708 | |
| 1635 | "Update consecutive item numbering in the current category." | 1709 | ;; FIXME: ??? with todos-lower-item leaves overlay of lower item if this is |
| 1636 | (save-excursion | 1710 | ;; the third or greater item number -- but not in edebug |
| 1637 | (goto-char (point-min)) | 1711 | ;; (defun todos-update-numbered-prefix () |
| 1638 | (while (not (eobp)) | 1712 | ;; "Update consecutive item numbering in the current category." |
| 1639 | (remove-overlays (point) (point) 'before-string) | 1713 | ;; (save-excursion |
| 1640 | (todos-forward-item)) | 1714 | ;; (goto-char (point-min)) |
| 1641 | (todos-show))) | 1715 | ;; (while (not (eobp)) |
| 1716 | ;; (let ((ov (car (overlays-in (point) (point)))) | ||
| 1717 | ;; val) | ||
| 1718 | ;; (when ov | ||
| 1719 | ;; (setq val (overlay-get ov 'before-string)) | ||
| 1720 | ;; (remove-overlays (point) (point) 'before-string val))) | ||
| 1721 | ;; (todos-forward-item)) | ||
| 1722 | ;; (todos-show))) | ||
| 1723 | |||
| 1724 | ;; (defun todos-update-numbered-prefix () | ||
| 1725 | ;; "Update consecutive item numbering in the current category." | ||
| 1726 | ;; (save-excursion | ||
| 1727 | ;; (goto-char (point-min)) | ||
| 1728 | ;; (while (not (eobp)) | ||
| 1729 | ;; (remove-overlays (point) (point)) | ||
| 1730 | ;; (todos-forward-item)) | ||
| 1731 | ;; ;; FIXME: is todos-prefix-overlays enough? | ||
| 1732 | ;; (todos-show))) | ||
| 1642 | 1733 | ||
| 1643 | (defvar todos-item-start-overlays nil "") | 1734 | ;; (defvar todos-item-start-overlays nil "") |
| 1644 | 1735 | ||
| 1645 | ;; (defvar todos-done-overlays nil "") | 1736 | ;; (defvar todos-done-overlays nil "") |
| 1646 | 1737 | ||
| 1647 | ;; (defun todos-check-overlay (prop) | ||
| 1648 | ;; "" ;FIXME add docstring | ||
| 1649 | ;; ;; (let ((ovlist (overlays-in (1- (point)) (1+ (point))))) | ||
| 1650 | ;; (let ((ovlist (overlays-in (point) (point)))) | ||
| 1651 | ;; (when ovlist (overlay-get (car ovlist) prop)))) | ||
| 1652 | |||
| 1653 | (defun todos-prefix-overlays () | 1738 | (defun todos-prefix-overlays () |
| 1654 | "" ;FIXME add docstring | 1739 | "" |
| 1655 | (when (or todos-number-prefix | 1740 | (when (or todos-number-prefix |
| 1656 | (not (string-match "^[[:space:]]*$" todos-prefix))) | 1741 | (not (string-match "^[[:space:]]*$" todos-prefix))) |
| 1657 | (let ((prefix (propertize (concat todos-prefix " ") 'face 'todos-prefix-string)) | 1742 | (let ((prefix (propertize (concat todos-prefix " ") 'face 'todos-prefix-string)) |
| 1658 | (num 0) | 1743 | (num 0)) |
| 1659 | lim ov-pref) | ||
| 1660 | (save-excursion | 1744 | (save-excursion |
| 1661 | (goto-char (point-min)) | 1745 | (goto-char (point-min)) |
| 1662 | (while (or (todos-date-string-match lim) | 1746 | (while (not (eobp)) |
| 1663 | (todos-done-string-match lim)) | 1747 | (when (or (todos-date-string-match (line-end-position)) |
| 1664 | (goto-char (match-beginning 0)) | 1748 | (todos-done-string-match (line-end-position))) |
| 1665 | (when todos-number-prefix | 1749 | (goto-char (match-beginning 0)) |
| 1666 | (setq num (1+ num)) | 1750 | (when todos-number-prefix |
| 1667 | ;; reset number for done items | 1751 | (setq num (1+ num)) |
| 1668 | (if (or (looking-at (concat "\n\\[" (regexp-quote todos-done-string))) | 1752 | ;; reset number for done items |
| 1669 | ;; if last not done item is multiline, then | 1753 | (when ;; (or |
| 1670 | ;; todos-done-string-match skips empty line, so have | 1754 | ;; ;; FIXME: really need this? |
| 1671 | ;; to look back | 1755 | ;; (looking-at (concat "\n\\[" (regexp-quote todos-done-string))) |
| 1672 | (and (looking-at (concat "^\\[" (regexp-quote todos-done-string))) | 1756 | ;; if last not done item is multiline, then |
| 1673 | (looking-back "\n\n"))) | 1757 | ;; todos-done-string-match skips empty line, so have |
| 1674 | (setq num 1)) | 1758 | ;; to look back. |
| 1675 | (setq prefix (propertize (concat (number-to-string num) " ") | 1759 | (and (looking-at (concat "^\\[" (regexp-quote todos-done-string))) |
| 1676 | 'face 'todos-prefix-string))) | 1760 | (looking-back "\n\n"));) |
| 1677 | (or (and (setq ov-pref (car (overlays-in (point) (point)))) | 1761 | (setq num 1)) |
| 1678 | (equal (overlay-get ov-pref 'before-string) prefix)) | 1762 | (setq prefix (propertize (concat (number-to-string num) " ") |
| 1679 | (and (setq ov-pref (pop todos-item-start-overlays)) | 1763 | 'face 'todos-prefix-string))) |
| 1680 | (move-overlay ov-pref (point) (point))) | 1764 | ;; (let ((ovs (overlays-in (point) (point)))) |
| 1681 | (and (setq ov-pref (make-overlay (point) (point))) | 1765 | ;; (or (and (setq ov-pref (car ovs)) |
| 1682 | (overlay-put ov-pref 'before-string prefix))) | 1766 | ;; ;; when done-separator overlay is in front of prefix overlay |
| 1683 | (forward-line)))))) | 1767 | ;; (if (and (> (length ovs) 1) |
| 1684 | 1768 | ;; (not (equal (overlay-get ov-pref 'before-string) | |
| 1685 | ;; (defun todos-show-paren-hack () | 1769 | ;; prefix))) |
| 1686 | ;; "Purge overlay duplication due to show-paren-mode." | 1770 | ;; (setq ov-pref (cadr ovs)) |
| 1687 | ;; (save-excursion | 1771 | ;; t) |
| 1688 | ;; (when show-paren-mode | 1772 | ;; (equal (overlay-get ov-pref 'before-string) prefix)) |
| 1689 | ;; (goto-char (point-min)) | 1773 | ;; ;; non-numerical prefix |
| 1690 | ;; (while (not (eobp)) | 1774 | ;; (and (setq ov-pref (pop todos-item-start-overlays)) |
| 1691 | ;; ;; (let ((ovlist (overlays-in (1- (point)) (1+ (point)))) | 1775 | ;; (move-overlay ov-pref (point) (point))) |
| 1692 | ;; (let ((ovlist (overlays-in (point) (point))) | 1776 | ;; (and (setq ov-pref (make-overlay (point) (point))) |
| 1693 | ;; ov) | 1777 | ;; (overlay-put ov-pref 'before-string prefix)))) |
| 1694 | ;; (while (> (length ovlist) 1) | 1778 | (let* ((ovs (overlays-in (point) (point))) |
| 1695 | ;; (setq ov (pop ovlist)) | 1779 | (ov-pref (car ovs)) |
| 1696 | ;; (delete-overlay ov))) | 1780 | (val (when ov-pref (overlay-get ov-pref 'before-string)))) |
| 1697 | ;; (forward-line)) | 1781 | (when (and (> (length ovs) 1) |
| 1698 | ;; (if (and (bolp) (eolp)) | 1782 | (not (equal val prefix))) |
| 1699 | ;; ;; (let ((ovlist (overlays-in (1- (point)) (1+ (point))))) | 1783 | (setq ov-pref (cadr ovs))) |
| 1700 | ;; (let ((ovlist (overlays-in (point) (point)))) | 1784 | (when (not (equal val prefix)) |
| 1701 | ;; (remove-overlays (1- (point)) (1+ (point)))))))) | 1785 | ;; (delete-overlay ov-pref) |
| 1786 | (remove-overlays (point) (point)); 'before-string val) | ||
| 1787 | (setq ov-pref (make-overlay (point) (point))) | ||
| 1788 | (overlay-put ov-pref 'before-string prefix)))) | ||
| 1789 | (forward-line)))))) | ||
| 1702 | 1790 | ||
| 1703 | (defun todos-reset-separator (symbol value) | 1791 | (defun todos-reset-separator (symbol value) |
| 1704 | "Set SYMBOL's value to VALUE, and ." ; FIXME | 1792 | "Set SYMBOL's value to VALUE, and ." ; FIXME |
| @@ -1710,19 +1798,24 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1710 | (todos-show) | 1798 | (todos-show) |
| 1711 | (save-excursion | 1799 | (save-excursion |
| 1712 | (goto-char (point-min)) | 1800 | (goto-char (point-min)) |
| 1713 | (when (re-search-forward (concat "^\\[" (regexp-quote todos-done-string)) nil t) | 1801 | (when (re-search-forward (concat "^\\[" (regexp-quote todos-done-string)) |
| 1802 | nil t) | ||
| 1714 | (remove-overlays (point) (point)))) | 1803 | (remove-overlays (point) (point)))) |
| 1715 | ;; activate the prefix setting (save-restriction does not help) | 1804 | ;; activate the prefix setting (save-restriction does not help) |
| 1716 | (todos-show))))) | 1805 | (todos-show))))) |
| 1717 | 1806 | ||
| 1718 | ;; FIXME: use this; should be defsubst? | 1807 | ;; FIXME: should be defsubst? |
| 1808 | (defun todos-category-number (cat) | ||
| 1809 | "Set todos-category-number to index of CAT in todos-categories." | ||
| 1810 | (setq todos-category-number (- (length todos-categories) | ||
| 1811 | (length (member cat todos-categories))))) | ||
| 1719 | (defun todos-current-category () | 1812 | (defun todos-current-category () |
| 1720 | "Return the name of the current category." | 1813 | "Return the name of the current category." |
| 1721 | (nth todos-category-number todos-categories)) | 1814 | (nth todos-category-number todos-categories)) |
| 1722 | 1815 | ||
| 1723 | (defun todos-category-select () | 1816 | (defun todos-category-select () |
| 1724 | "Make TODO mode display the current category correctly." | 1817 | "Make TODO mode display the current category correctly." |
| 1725 | (let ((name (nth todos-category-number todos-categories))) | 1818 | (let ((name (todos-current-category))) |
| 1726 | (setq mode-line-buffer-identification (concat "Category: " name)) | 1819 | (setq mode-line-buffer-identification (concat "Category: " name)) |
| 1727 | (widen) | 1820 | (widen) |
| 1728 | (goto-char (point-min)) | 1821 | (goto-char (point-min)) |
| @@ -1736,138 +1829,104 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1736 | (narrow-to-region begin end) | 1829 | (narrow-to-region begin end) |
| 1737 | (goto-char (point-min)))) | 1830 | (goto-char (point-min)))) |
| 1738 | (todos-prefix-overlays) | 1831 | (todos-prefix-overlays) |
| 1739 | (let ((beg (point-min)) | 1832 | ;; display or hide done items as per todos-show-with-done |
| 1740 | (done-sep (if (string-match "^[[:space:]]*$" todos-done-separator) | 1833 | (save-excursion |
| 1741 | todos-done-separator | ||
| 1742 | (propertize (concat todos-done-separator "\n") | ||
| 1743 | 'face 'todos-done-sep))) | ||
| 1744 | done ov) | ||
| 1745 | (when (re-search-forward (concat "\n\\(\\[" (regexp-quote todos-done-string) | 1834 | (when (re-search-forward (concat "\n\\(\\[" (regexp-quote todos-done-string) |
| 1746 | "\\)") nil t) | 1835 | "\\)") nil t) |
| 1747 | (setq done (match-beginning 1) | 1836 | (let (done end done-sep prefix ov-pref ov-done) |
| 1748 | end (match-beginning 0)) | 1837 | (setq done (match-beginning 1) |
| 1749 | (if todos-show-with-done | 1838 | end (match-beginning 0)) |
| 1750 | ;; with an empty separator just display the done items | 1839 | (if todos-show-with-done |
| 1751 | (if (string= done-sep todos-done-separator) | 1840 | (progn |
| 1752 | (narrow-to-region (point-min) (point-max)) | 1841 | (setq done-sep todos-done-separator) |
| 1753 | ;; else display the separator in an overlay in front of the prefix | 1842 | (unless (string-match "^[[:space:]]*$" todos-done-separator) |
| 1754 | ;; overlay on first done item | 1843 | (setq done-sep (propertize (concat todos-done-separator "\n") |
| 1755 | (let ((prefix (propertize | 1844 | 'face 'todos-done-sep)) |
| 1756 | (concat (if todos-number-prefix "1" todos-prefix) " ") | 1845 | (setq prefix (propertize |
| 1757 | 'face 'todos-prefix-string))) | 1846 | (concat (if todos-number-prefix "1" todos-prefix) " ") |
| 1758 | (goto-char done) | 1847 | 'face 'todos-prefix-string)) |
| 1759 | (remove-overlays done done) | 1848 | ;; FIXME? Just deleting done-sep overlay results in bad |
| 1760 | ;; must make separator overlay after making prefix overlay to get | 1849 | ;; display (except when stepping though in edebug) |
| 1761 | ;; the order separator before prefix | 1850 | (remove-overlays done done) |
| 1762 | (setq ov-pref (make-overlay done done) | 1851 | ;; must make separator overlay after making prefix overlay to get |
| 1763 | ov-done (make-overlay done done)) | 1852 | ;; the order separator before prefix |
| 1764 | (overlay-put ov-pref 'before-string prefix) | 1853 | (setq ov-pref (make-overlay done done) |
| 1765 | (overlay-put ov-done 'before-string done-sep))) | 1854 | ov-done (make-overlay done done)) |
| 1766 | ;; hide done items | 1855 | (overlay-put ov-pref 'before-string prefix) |
| 1767 | (narrow-to-region (point-min) end)))) | 1856 | (overlay-put ov-done 'before-string done-sep))) |
| 1768 | (goto-char (point-min))) | 1857 | (narrow-to-region (point-min) end)))))) |
| 1769 | 1858 | ||
| 1770 | ;; FIXME: using numbering for priority instead of importance? | 1859 | ;; FIXME: why autoload? |
| 1771 | ;;;###autoload | 1860 | ;;;###autoload |
| 1772 | (defun todos-add-item-non-interactively (new-item category) | 1861 | ;; (defun todos-add-item-non-interactively (item category) |
| 1773 | "Insert NEW-ITEM in TODO list as a new entry in CATEGORY." | 1862 | ;; "Insert item ITEM into category CATEGORY and set its priority." |
| 1774 | ;; FIXME: really need this? (and in save-excursion?) | 1863 | ;; (todos-category-number category) |
| 1775 | (save-excursion | 1864 | ;; (todos-show) ; now at point-min |
| 1776 | (todos-show)) | 1865 | ;; (unless (or (eq (point-min) (point-max)) ; no unfinished items |
| 1777 | (if (string= "" category) | 1866 | ;; (when (re-search-forward (concat "^\\[" |
| 1778 | (setq category (nth todos-category-number todos-categories))) | 1867 | ;; (regexp-quote todos-done-string)) |
| 1779 | (let ((cat-exists (member category todos-categories))) | 1868 | ;; nil t) |
| 1780 | (setq todos-category-number | 1869 | ;; (forward-line -1) |
| 1781 | (if cat-exists | 1870 | ;; (bobp))) ; there are done items but no unfinished items |
| 1782 | (- (length todos-categories) (length cat-exists)) | 1871 | ;; (let* ((maxnum (1+ (car (todos-item-counts category)))) |
| 1783 | (todos-add-category category)))) | 1872 | ;; priority candidate prompt) |
| 1784 | ;; FIXME: really need this? (yes for todos-move-item, to show moved to category) | 1873 | ;; (while (null priority) |
| 1785 | (todos-show) ; now at point-min | 1874 | ;; (setq candidate |
| 1786 | ;; (setq todos-previous-line 0) | 1875 | ;; (string-to-number (read-from-minibuffer |
| 1787 | ;; (let* ((top 1) | 1876 | ;; (concat prompt |
| 1788 | ;; (end (save-excursion | 1877 | ;; (format "Set item priority (1-%d): " |
| 1789 | ;; (goto-char (point-min)) | 1878 | ;; maxnum))))) |
| 1790 | ;; (if (re-search-forward (concat "\n\n\\\(\\[" | 1879 | ;; (setq prompt |
| 1791 | ;; (regexp-quote todos-done-string) | 1880 | ;; (when (or (< candidate 1) (> candidate maxnum)) |
| 1792 | ;; "\\)") nil t) | 1881 | ;; (format "Priority must be an integer between 1 and %d.\n" maxnum))) |
| 1793 | ;; (match-beginning 1) | 1882 | ;; (unless prompt (setq priority candidate))) |
| 1794 | ;; (point-max)))) | 1883 | ;; (goto-char (point-min)) |
| 1795 | ;; (bottom (count-lines (point-min) end))) | 1884 | ;; (unless (= priority 1) (todos-forward-item (1- priority))))) |
| 1796 | ;; (while (> (- bottom top) todos-insert-threshold) | 1885 | ;; (todos-insert-with-overlays item)) |
| 1797 | ;; (let* ((current (/ (+ top bottom) 2)) | 1886 | |
| 1798 | ;; (answer (if (< current bottom) | 1887 | (defun todos-set-item-priority (item cat) |
| 1799 | ;; (todos-more-important-p current) nil))) | 1888 | "Set the priority of unfinished item ITEM in category CAT." |
| 1800 | ;; (if answer | 1889 | (todos-category-number cat) |
| 1801 | ;; (setq bottom current) | 1890 | (todos-category-select) |
| 1802 | ;; (setq top (1+ current))))) | 1891 | (let* ((not-done (car (todos-item-counts cat))) |
| 1803 | ;; (setq top (/ (+ top bottom) 2)) | 1892 | (maxnum (1+ not-done)) |
| 1804 | ;; (goto-char (point-min)) | 1893 | priority candidate prompt) |
| 1805 | ;; (forward-line (1- top))) | 1894 | (unless (zerop not-done) |
| 1806 | (unless (or (eq (point-min) (point-max)) ; no unfinished items | 1895 | (while (null priority) |
| 1807 | (when (re-search-forward (concat "^\\[" | 1896 | (setq candidate |
| 1808 | (regexp-quote todos-done-string)) | ||
| 1809 | nil t) | ||
| 1810 | (forward-line -1) | ||
| 1811 | (bobp))) ; there are done items but no unfinished items | ||
| 1812 | (let* ((num-items (1+ (car (todos-count-items-in-category)))) | ||
| 1813 | (priority (string-to-number (read-from-minibuffer | ||
| 1814 | (format "Set item priority (1-%d): " | ||
| 1815 | num-items)))) | ||
| 1816 | prompt) | ||
| 1817 | (while (cond ((not (integerp priority)) | ||
| 1818 | (setq prompt "Priority must be an integer.\n")) | ||
| 1819 | ((< priority 1) | ||
| 1820 | (setq prompt "Priority cannot be higher than 1.\n")) | ||
| 1821 | ((> priority num-items) | ||
| 1822 | (setq prompt (format "Priority cannot be lower than %d.\n" | ||
| 1823 | num-items)))) | ||
| 1824 | (setq priority | ||
| 1825 | (string-to-number (read-from-minibuffer | 1897 | (string-to-number (read-from-minibuffer |
| 1826 | (concat prompt | 1898 | (concat prompt |
| 1827 | (format "Set item priority (1-%d): " | 1899 | (format "Set item priority (1-%d): " |
| 1828 | num-items)))))) | 1900 | maxnum))))) |
| 1901 | (setq prompt | ||
| 1902 | (when (or (< candidate 1) (> candidate maxnum)) | ||
| 1903 | (format "Priority must be an integer between 1 and %d.\n" maxnum))) | ||
| 1904 | (unless prompt (setq priority candidate))) | ||
| 1829 | (goto-char (point-min)) | 1905 | (goto-char (point-min)) |
| 1830 | (todos-forward-item (1- priority)))) | 1906 | (unless (= priority 1) (todos-forward-item (1- priority)))))) |
| 1831 | (todos-insert-with-overlays new-item)) | ||
| 1832 | 1907 | ||
| 1833 | (defun todos-jump-to-category-noninteractively (cat) | 1908 | (defun todos-jump-to-category-noninteractively (cat) |
| 1909 | "" | ||
| 1834 | (let ((bufname (buffer-name))) | 1910 | (let ((bufname (buffer-name))) |
| 1835 | (cond ((string= bufname todos-categories-buffer) | 1911 | (cond ((string= bufname todos-categories-buffer) |
| 1836 | (switch-to-buffer (file-name-nondirectory todos-file-do))) | 1912 | (switch-to-buffer (file-name-nondirectory todos-file-do))) |
| 1837 | ((string= bufname todos-archived-categories-buffer) | 1913 | ((string= bufname todos-archived-categories-buffer) |
| 1838 | ;; Is pop-to-buffer better for this case? | 1914 | ;; FIXME: is pop-to-buffer better for this case? |
| 1839 | (switch-to-buffer (file-name-nondirectory todos-archive-file)))) | 1915 | (switch-to-buffer (file-name-nondirectory todos-archive-file)))) |
| 1840 | (kill-buffer bufname)) | 1916 | (kill-buffer bufname)) |
| 1841 | (widen) | 1917 | (widen) |
| 1842 | (goto-char (point-min)) | 1918 | (goto-char (point-min)) |
| 1843 | (setq todos-category-number (- (length todos-categories) | 1919 | (todos-category-number cat) |
| 1844 | (length (member cat todos-categories)))) | ||
| 1845 | (todos-category-select)) | 1920 | (todos-category-select)) |
| 1846 | 1921 | ||
| 1847 | (defun todos-insert-with-overlays (item) | 1922 | (defun todos-insert-with-overlays (item) |
| 1848 | "" ;FIXME add docstring | 1923 | "" |
| 1849 | ;; FIXME: breaks without narrowing, e.g. todos-item-done | 1924 | (todos-item-start) |
| 1850 | ;; (unless (and (bolp) (eolp)) (goto-char (todos-item-start))) | ||
| 1851 | (insert item "\n") | 1925 | (insert item "\n") |
| 1852 | (todos-backward-item) | 1926 | (todos-backward-item) |
| 1853 | (if todos-number-prefix | 1927 | ;; (if todos-number-prefix |
| 1854 | (todos-update-numbered-prefix) | 1928 | ;; (todos-update-numbered-prefix) |
| 1855 | (todos-prefix-overlays))) | 1929 | (todos-prefix-overlays));) |
| 1856 | |||
| 1857 | ;; (defun todos-more-important-p (line) | ||
| 1858 | ;; "Ask whether entry is more important than the one at LINE." | ||
| 1859 | ;; (unless (equal todos-previous-line line) | ||
| 1860 | ;; (setq todos-previous-line line) | ||
| 1861 | ;; (goto-char (point-min)) | ||
| 1862 | ;; (forward-line (1- todos-previous-line)) | ||
| 1863 | ;; (let ((item (todos-item-string-start))) | ||
| 1864 | ;; (setq todos-previous-answer | ||
| 1865 | ;; (y-or-n-p (concat "More important than '" item "'? "))))) | ||
| 1866 | ;; todos-previous-answer) | ||
| 1867 | |||
| 1868 | (defun todos-line-string () | ||
| 1869 | "Return current line in buffer as a string." | ||
| 1870 | (buffer-substring (line-beginning-position) (line-end-position))) | ||
| 1871 | 1930 | ||
| 1872 | (defun todos-item-string-start () | 1931 | (defun todos-item-string-start () |
| 1873 | "Return the start of this TODO list entry as a string." | 1932 | "Return the start of this TODO list entry as a string." |
| @@ -1877,33 +1936,40 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1877 | (setq item (concat (substring item 0 56) "..."))) | 1936 | (setq item (concat (substring item 0 56) "..."))) |
| 1878 | item)) | 1937 | item)) |
| 1879 | 1938 | ||
| 1939 | (defvar todos-item-start (concat "^\\(\\[\\(" (regexp-quote todos-done-string) | ||
| 1940 | "\\)?\\)?" todos-date-pattern) | ||
| 1941 | "String identifying start of a Todos item.") | ||
| 1942 | |||
| 1880 | (defun todos-item-start () | 1943 | (defun todos-item-start () |
| 1881 | "Move to start of current TODO list item and return its position." | 1944 | "Move to start of current TODO list item and return its position." |
| 1882 | (unless (or (looking-at "^$") ; last item or between done and not done | 1945 | (unless (or (looking-at "^$") ; last item or between done and not done |
| 1883 | (looking-at (regexp-quote todos-category-beg))) ; for todos-count-items | 1946 | (looking-at (regexp-quote todos-category-beg))) ; for todos-count-items |
| 1884 | (goto-char (line-beginning-position)) | 1947 | (goto-char (line-beginning-position)) |
| 1885 | (while (not (looking-at (concat "^\\(\\[\\(" (regexp-quote todos-done-string) | 1948 | (while (not (looking-at todos-item-start)) |
| 1886 | "\\)?\\)?" todos-date-pattern))) | ||
| 1887 | (forward-line -1))) | 1949 | (forward-line -1))) |
| 1888 | (point)) | 1950 | (point)) |
| 1889 | 1951 | ||
| 1890 | (defun todos-item-end () | 1952 | (defun todos-item-end () |
| 1891 | "Move to end of current TODO list item and return its position." | 1953 | "Move to end of current TODO list item and return its position." |
| 1892 | (unless (looking-at "^$") ; last item or between done and not done | 1954 | (unless (looking-at "^$") ; FIXME: |
| 1893 | (todos-forward-item) | 1955 | (let ((done (todos-done-item-p))) |
| 1894 | (backward-char)) | 1956 | (todos-forward-item) |
| 1957 | ;; adjust if item is last unfinished one before displayed done items | ||
| 1958 | (when (and (not done) (todos-done-item-p)) | ||
| 1959 | (forward-line -1)) | ||
| 1960 | (backward-char))) | ||
| 1895 | (point)) | 1961 | (point)) |
| 1896 | 1962 | ||
| 1897 | (defun todos-remove-item () | 1963 | (defun todos-remove-item () |
| 1898 | "Delete the current entry from the TODO list." | 1964 | "Delete the current entry from the TODO list." |
| 1899 | (let* ((end (progn (todos-forward-item) (point))) | 1965 | (let* ((beg (todos-item-start)) |
| 1900 | (beg (progn (todos-backward-item) (point))) | 1966 | (end (progn (todos-item-end) (1+ (point)))) |
| 1901 | (ov-start (car (overlays-in beg beg)))) | 1967 | (ov-start (car (overlays-in beg beg)))) |
| 1902 | (when ov-start | 1968 | (when ov-start |
| 1903 | ;; don't cache numbers, since they can be popped out of order in | 1969 | ;; ;; don't cache numbers, since they can be popped out of order in |
| 1904 | ;; todos-prefix-overlays | 1970 | ;; ;; todos-prefix-overlays |
| 1905 | (unless todos-number-prefix | 1971 | ;; (unless todos-number-prefix |
| 1906 | (push ov-start todos-item-start-overlays)) | 1972 | ;; (push ov-start todos-item-start-overlays)) |
| 1907 | (delete-overlay ov-start)) | 1973 | (delete-overlay ov-start)) |
| 1908 | (delete-region beg end))) | 1974 | (delete-region beg end))) |
| 1909 | 1975 | ||
| @@ -1912,38 +1978,16 @@ Number of entries for each category is given by `todos-print-priorities'." | |||
| 1912 | (buffer-substring (todos-item-start) (todos-item-end))) | 1978 | (buffer-substring (todos-item-start) (todos-item-end))) |
| 1913 | 1979 | ||
| 1914 | (defun todos-done-item-p () | 1980 | (defun todos-done-item-p () |
| 1915 | "" ;FIXME | 1981 | "" |
| 1916 | (save-excursion | 1982 | (save-excursion |
| 1917 | (todos-item-start) | 1983 | (todos-item-start) |
| 1918 | (looking-at (concat "^\\[" (regexp-quote todos-done-string))))) | 1984 | (looking-at (concat "^\\[" (regexp-quote todos-done-string))))) |
| 1919 | 1985 | ||
| 1920 | (defun todos-count-items-in-category () | 1986 | |
| 1921 | "Return number of not done and done items in current category." | 1987 | (defvar todos-categories-alist nil |
| 1922 | (save-excursion | 1988 | "Variable for storing the result of todos-make-categories-alist.") |
| 1923 | (let ((not-done 0) | 1989 | (defun todos-make-categories-alist () |
| 1924 | (done 0) | 1990 | "Return an alist of categories and some of their properties. |
| 1925 | (beg (point-min)) | ||
| 1926 | end) | ||
| 1927 | (save-restriction | ||
| 1928 | (widen) | ||
| 1929 | (re-search-forward (concat "^" (regexp-quote todos-category-beg)) nil t) | ||
| 1930 | (setq end (or (match-beginning 0) (point-max))) | ||
| 1931 | (goto-char beg) | ||
| 1932 | (while (> end (point)) | ||
| 1933 | (if (todos-done-item-p) | ||
| 1934 | (setq done (1+ done)) | ||
| 1935 | (setq not-done (1+ not-done))) | ||
| 1936 | (todos-forward-item) | ||
| 1937 | (when (and (not (> (point) end)) | ||
| 1938 | (looking-at "^$") | ||
| 1939 | (not (eobp))) | ||
| 1940 | ;; point is between done and not done items | ||
| 1941 | (setq not-done (1- not-done)))) | ||
| 1942 | (cons not-done done))))) | ||
| 1943 | |||
| 1944 | ;; FIXME: rename, since *-alist is by convention a variable name | ||
| 1945 | (defun todos-categories-alist () | ||
| 1946 | "Return alist of categories and some of their properties. | ||
| 1947 | The properties are at least the numbers of the unfinished and | 1991 | The properties are at least the numbers of the unfinished and |
| 1948 | done items in the category." | 1992 | done items in the category." |
| 1949 | (let (todos-categories-alist) | 1993 | (let (todos-categories-alist) |
| @@ -1969,19 +2013,34 @@ done items in the category." | |||
| 1969 | (forward-line))))) | 2013 | (forward-line))))) |
| 1970 | todos-categories-alist)) | 2014 | todos-categories-alist)) |
| 1971 | 2015 | ||
| 1972 | (defun todos-count-all-items () | 2016 | (defun todos-item-counts (cat &optional how) |
| 1973 | "" | 2017 | "" |
| 1974 | (let ((unfinished 0) | 2018 | (let* ((counts (cadr (assoc cat todos-categories-alist))) |
| 1975 | (done 0)) | 2019 | (not-done (car counts)) |
| 1976 | (dolist (l (todos-categories-alist)) | 2020 | (done (cdr counts))) |
| 1977 | (setq unfinished (+ unfinished (car (cadr l))) | 2021 | (cond ((eq how 'insert) |
| 1978 | done (+ done (cdr (cadr l))))) | 2022 | (setcar counts (1+ not-done))) |
| 1979 | (cons unfinished done))) | 2023 | ((eq how 'delete) |
| 1980 | 2024 | (if (todos-done-item-p) ;FIXME: fails if last done item was deleted | |
| 1981 | (defun todos-longest-category-name-length () | 2025 | (setcdr counts (1- done)) |
| 2026 | (setcar counts (1- not-done)))) | ||
| 2027 | ;; ((eq how 'move-failed) | ||
| 2028 | ;; (setcar counts not-done)) | ||
| 2029 | ((eq how 'done) | ||
| 2030 | (setcar counts (1- not-done)) | ||
| 2031 | (setcdr counts (1+ done))) | ||
| 2032 | ((eq how 'undo) | ||
| 2033 | (setcar counts (1+ not-done)) | ||
| 2034 | (setcdr counts (1- done))) | ||
| 2035 | ((eq how 'archive) | ||
| 2036 | (setcdr counts 0)) | ||
| 2037 | (t | ||
| 2038 | (cons not-done done))))) | ||
| 2039 | |||
| 2040 | (defun todos-longest-category-name-length (categories) | ||
| 1982 | "" | 2041 | "" |
| 1983 | (let ((longest 0)) | 2042 | (let ((longest 0)) |
| 1984 | (dolist (c (todos-categories-alist) longest) | 2043 | (dolist (c categories longest) |
| 1985 | (setq longest (max longest (length (car c))))))) | 2044 | (setq longest (max longest (length (car c))))))) |
| 1986 | 2045 | ||
| 1987 | (defun todos-string-count-lines (string) | 2046 | (defun todos-string-count-lines (string) |
| @@ -1992,8 +2051,8 @@ done items in the category." | |||
| 1992 | "Return non-nil if STRING spans several lines." | 2051 | "Return non-nil if STRING spans several lines." |
| 1993 | (> (todos-string-count-lines string) 1)) | 2052 | (> (todos-string-count-lines string) 1)) |
| 1994 | 2053 | ||
| 1995 | (defun todos-completing-read () | 2054 | (defun todos-read-category () |
| 1996 | "Return a category name, with completion, for use in Todo mode." | 2055 | "Return an existing category name, with tab completion." |
| 1997 | ;; allow SPC to insert spaces, for adding new category names with | 2056 | ;; allow SPC to insert spaces, for adding new category names with |
| 1998 | ;; todos-move-item | 2057 | ;; todos-move-item |
| 1999 | (let ((map minibuffer-local-completion-map)) | 2058 | (let ((map minibuffer-local-completion-map)) |
| @@ -2002,7 +2061,7 @@ done items in the category." | |||
| 2002 | ;; non-nil, which makes completing-read alter todos-categories | 2061 | ;; non-nil, which makes completing-read alter todos-categories |
| 2003 | (let* ((categories (copy-sequence todos-categories)) | 2062 | (let* ((categories (copy-sequence todos-categories)) |
| 2004 | (history (cons 'todos-categories (1+ todos-category-number))) | 2063 | (history (cons 'todos-categories (1+ todos-category-number))) |
| 2005 | (default (nth todos-category-number todos-categories)) | 2064 | (default (todos-current-category)) ;FIXME: why this default? |
| 2006 | (completion-ignore-case todos-completion-ignore-case) | 2065 | (completion-ignore-case todos-completion-ignore-case) |
| 2007 | (category (completing-read | 2066 | (category (completing-read |
| 2008 | (concat "Category [" default "]: ") | 2067 | (concat "Category [" default "]: ") |
| @@ -2011,6 +2070,62 @@ done items in the category." | |||
| 2011 | (setq todos-categories categories) | 2070 | (setq todos-categories categories) |
| 2012 | category))) | 2071 | category))) |
| 2013 | 2072 | ||
| 2073 | (defun todos-check-category-name (cat) | ||
| 2074 | "Reject names for category CAT that could yield bugs or confusion." | ||
| 2075 | (let (prompt) | ||
| 2076 | (while (and (cond ((string= "" cat) | ||
| 2077 | (setq prompt "Enter a non-empty category name: ")) | ||
| 2078 | ((string-match "\\`\\s-+\\'" cat) | ||
| 2079 | (setq prompt "Enter a category name that is not only white space: ")) | ||
| 2080 | ((member cat todos-categories) | ||
| 2081 | (setq prompt "Enter a non-existing category name: "))) | ||
| 2082 | (setq cat (read-from-minibuffer prompt))))) | ||
| 2083 | cat) | ||
| 2084 | |||
| 2085 | ;; adapted from calendar-read-date | ||
| 2086 | (defun todos-read-date () | ||
| 2087 | "Prompt for Gregorian date and return it in the current format." | ||
| 2088 | (let* ((year (calendar-read | ||
| 2089 | "Year (>0): " | ||
| 2090 | (lambda (x) (> x 0)) | ||
| 2091 | (number-to-string (calendar-extract-year | ||
| 2092 | (calendar-current-date))))) | ||
| 2093 | (month-array calendar-month-name-array) | ||
| 2094 | (completion-ignore-case t) | ||
| 2095 | (month (cdr (assoc-string | ||
| 2096 | (completing-read | ||
| 2097 | "Month name (RET for current month): " | ||
| 2098 | (mapcar 'list (append month-array nil)) | ||
| 2099 | nil t nil nil | ||
| 2100 | (calendar-month-name (calendar-extract-month | ||
| 2101 | (calendar-current-date)))) | ||
| 2102 | (calendar-make-alist month-array 1) t))) | ||
| 2103 | (last (calendar-last-day-of-month month year)) | ||
| 2104 | day) | ||
| 2105 | (while (or (not (numberp day)) (< day 0) (< last day)) | ||
| 2106 | (setq day (read-from-minibuffer | ||
| 2107 | (format "Day (1-%d): " last) nil nil t nil | ||
| 2108 | (number-to-string (calendar-extract-day (calendar-current-date)))))) | ||
| 2109 | (calendar-date-string (list month day year) t t))) | ||
| 2110 | |||
| 2111 | (defun todos-read-dayname () | ||
| 2112 | "" | ||
| 2113 | (let ((completion-ignore-case t)) | ||
| 2114 | (completing-read "Enter a day name: " | ||
| 2115 | (append calendar-day-name-array nil) | ||
| 2116 | nil t))) | ||
| 2117 | |||
| 2118 | (defun todos-read-time () | ||
| 2119 | "" | ||
| 2120 | (let (valid answer) | ||
| 2121 | (while (not valid) | ||
| 2122 | (setq answer (read-from-minibuffer | ||
| 2123 | "Enter a clock time: ")) | ||
| 2124 | (when (or (string= "" answer) | ||
| 2125 | (string-match diary-time-regexp answer)) | ||
| 2126 | (setq valid t))) | ||
| 2127 | answer)) | ||
| 2128 | |||
| 2014 | (defun todos-categories-list (buf) | 2129 | (defun todos-categories-list (buf) |
| 2015 | "Return a list of the Todo mode categories in buffer BUF." | 2130 | "Return a list of the Todo mode categories in buffer BUF." |
| 2016 | (let (categories) | 2131 | (let (categories) |
| @@ -2024,136 +2139,35 @@ done items in the category." | |||
| 2024 | (push (match-string-no-properties 1) categories))))) | 2139 | (push (match-string-no-properties 1) categories))))) |
| 2025 | categories)) | 2140 | categories)) |
| 2026 | 2141 | ||
| 2027 | ;; --------------------------------------------------------------------------- | 2142 | (defun todos-padded-string (str) |
| 2028 | ;;; Mode setup | 2143 | "" |
| 2029 | 2144 | (let* ((len (todos-longest-category-name-length todos-categories-alist)) | |
| 2030 | (easy-menu-define todos-menu todos-mode-map "Todo Menu" | 2145 | (strlen (length str)) |
| 2031 | '("Todo" | 2146 | (strlen-odd (eq (logand strlen 1) 1)) ; oddp from cl.el |
| 2032 | ["Next category" todos-forward-category t] | 2147 | (padding (/ (- len strlen) 2))) |
| 2033 | ["Previous category" todos-backward-category t] | 2148 | (concat (make-string padding 32) str |
| 2034 | ["Jump to category" todos-jump-to-category t] | 2149 | (make-string (if strlen-odd (1+ padding) padding) 32)))) |
| 2035 | ["Show top priority items" todos-top-priorities t] | 2150 | |
| 2036 | ["Print categories" todos-print t] | 2151 | (defun todos-insert-category-name (cat &optional nonum) |
| 2037 | "---" | 2152 | "" |
| 2038 | ["Edit item" todos-edit-item t] | 2153 | (let* ((buf (get-buffer (file-name-nondirectory todos-file-do))) |
| 2039 | ["File item" todos-file-item t] | 2154 | (cat-alist todos-categories-alist) |
| 2040 | ["Insert new item" todos-insert-item t] | 2155 | (counts (todos-item-counts cat))) |
| 2041 | ["Insert item here" todos-insert-item-here t] | 2156 | ;; num is declared in caller |
| 2042 | ["Kill item" todos-delete-item t] | 2157 | (setq num (1+ num)) |
| 2043 | "---" | 2158 | (if nonum |
| 2044 | ["Lower item priority" todos-lower-item t] | 2159 | (insert (make-string 4 32)) |
| 2045 | ["Raise item priority" todos-raise-item t] | 2160 | (insert " " (format "%2d" num) " ")) |
| 2046 | "---" | 2161 | (insert-button (todos-padded-string cat) |
| 2047 | ["Next item" todos-forward-item t] | 2162 | 'face 'todos-button |
| 2048 | ["Previous item" todos-backward-item t] | 2163 | 'action |
| 2049 | "---" | 2164 | `(lambda (button) |
| 2050 | ["Save" todos-save t] | 2165 | (todos-jump-to-category-noninteractively ,cat))) |
| 2051 | ["Save Top Priorities" todos-save-top-priorities t] | 2166 | (insert (make-string 8 32) |
| 2052 | "---" | 2167 | (format "%2d" (car counts)) |
| 2053 | ["Quit" todos-quit t] | 2168 | (make-string 5 32) |
| 2054 | )) | 2169 | (format "%2d" (cdr counts))) |
| 2055 | 2170 | (newline))) | |
| 2056 | ;; As calendar reads .todos-do before todos-mode is loaded. | ||
| 2057 | ;;;###autoload | ||
| 2058 | (defun todos-mode () | ||
| 2059 | "Major mode for editing TODO lists. | ||
| 2060 | |||
| 2061 | \\{todos-mode-map}" | ||
| 2062 | (interactive) | ||
| 2063 | (kill-all-local-variables) | ||
| 2064 | (setq major-mode 'todos-mode) | ||
| 2065 | (setq mode-name "TODOS") | ||
| 2066 | (use-local-map todos-mode-map) | ||
| 2067 | (easy-menu-add todos-menu) | ||
| 2068 | (when todos-wrap-lines (funcall todos-line-wrapping-function)) | ||
| 2069 | (make-local-variable 'font-lock-defaults) | ||
| 2070 | (setq font-lock-defaults '(todos-font-lock-keywords t)) | ||
| 2071 | (make-local-variable 'hl-line-range-function) | ||
| 2072 | (setq hl-line-range-function | ||
| 2073 | (lambda() (when (todos-item-end) | ||
| 2074 | (cons (todos-item-start) (todos-item-end))))) | ||
| 2075 | ;; (add-hook 'post-command-hook 'todos-show-paren-hack nil t) | ||
| 2076 | (add-to-invisibility-spec 'todos) | ||
| 2077 | ;; FIXME: use this and let-bind in editing commands? | ||
| 2078 | (setq buffer-read-only t) | ||
| 2079 | (run-mode-hooks 'todos-mode-hook)) | ||
| 2080 | |||
| 2081 | (defun todos-archive-mode () | ||
| 2082 | "Major mode for archived Todos categories. | ||
| 2083 | |||
| 2084 | \\{todos-mode-map}" | ||
| 2085 | (interactive) | ||
| 2086 | (kill-all-local-variables) | ||
| 2087 | (setq major-mode 'todos-archive-mode) | ||
| 2088 | (setq mode-name "TODOS Arch") | ||
| 2089 | (use-local-map todos-archive-mode-map) | ||
| 2090 | ;; (easy-menu-add todos-menu) | ||
| 2091 | (when todos-wrap-lines (funcall todos-line-wrapping-function)) | ||
| 2092 | (make-local-variable 'font-lock-defaults) | ||
| 2093 | (setq font-lock-defaults '(todos-font-lock-keywords t)) | ||
| 2094 | (make-local-variable 'hl-line-range-function) | ||
| 2095 | (setq hl-line-range-function | ||
| 2096 | (lambda() (when (todos-item-end) | ||
| 2097 | (cons (todos-item-start) (todos-item-end))))) | ||
| 2098 | ;; (add-hook 'post-command-hook 'todos-show-paren-hack nil t) | ||
| 2099 | (add-to-invisibility-spec 'todos) | ||
| 2100 | (run-mode-hooks 'todos-mode-hook)) | ||
| 2101 | |||
| 2102 | (defun todos-edit-mode () | ||
| 2103 | "Major mode for editing items in the TODO list. | ||
| 2104 | |||
| 2105 | \\{todos-edit-mode-map}" | ||
| 2106 | (interactive) | ||
| 2107 | (setq major-mode 'todos-edit-mode) | ||
| 2108 | (setq mode-name "TODOS Edit") | ||
| 2109 | (use-local-map todos-edit-mode-map) | ||
| 2110 | (make-local-variable 'font-lock-defaults) | ||
| 2111 | (setq font-lock-defaults '(todos-font-lock-keywords t)) | ||
| 2112 | (when todos-wrap-lines (funcall todos-line-wrapping-function))) | ||
| 2113 | |||
| 2114 | (defun todos-save () | ||
| 2115 | "Save the TODO list." | ||
| 2116 | (interactive) | ||
| 2117 | (save-excursion | ||
| 2118 | (save-restriction | ||
| 2119 | (save-buffer))) | ||
| 2120 | ;; (if todos-save-top-priorities-too (todos-save-top-priorities))) | ||
| 2121 | ) | ||
| 2122 | |||
| 2123 | (defun todos-quit () | ||
| 2124 | "Done with TODO list for now." | ||
| 2125 | (interactive) | ||
| 2126 | (widen) | ||
| 2127 | (todos-save) | ||
| 2128 | (message "") | ||
| 2129 | (bury-buffer)) | ||
| 2130 | |||
| 2131 | ;;;###autoload | ||
| 2132 | (defun todos-show () | ||
| 2133 | "Show TODO list." | ||
| 2134 | (interactive) | ||
| 2135 | ;; Make this a no-op if called interactively in narrowed Todos mode, since | ||
| 2136 | ;; it is in that case redundant, but in particular to work around the bug of | ||
| 2137 | ;; item prefix reduplication with show-paren-mode enabled. | ||
| 2138 | (unless (and (called-interactively-p) | ||
| 2139 | (eq major-mode 'todos-mode) | ||
| 2140 | (< (- ( point-max) (point-min)) (buffer-size))) | ||
| 2141 | ;; Call todos-initial-setup only if there is neither a Todo file nor | ||
| 2142 | ;; a corresponding unsaved buffer. | ||
| 2143 | (if (or (file-exists-p todos-file-do) | ||
| 2144 | (let* ((buf (get-buffer (file-name-nondirectory todos-file-do))) | ||
| 2145 | (bufname (buffer-file-name buf))) | ||
| 2146 | (equal (expand-file-name todos-file-do) bufname))) | ||
| 2147 | (find-file todos-file-do) | ||
| 2148 | (todos-initial-setup)) | ||
| 2149 | (unless (eq major-mode 'todos-mode) (todos-mode)) | ||
| 2150 | (unless todos-categories | ||
| 2151 | (setq todos-categories (todos-categories-list (buffer-name)))) | ||
| 2152 | ;; (beginning-of-line) | ||
| 2153 | (save-excursion | ||
| 2154 | (todos-category-select) | ||
| 2155 | ;; (todos-show-paren-hack) | ||
| 2156 | ))) | ||
| 2157 | 2171 | ||
| 2158 | (defun todos-initial-setup () | 2172 | (defun todos-initial-setup () |
| 2159 | "Set up things to work properly in TODO mode." | 2173 | "Set up things to work properly in TODO mode." |
| @@ -2164,5 +2178,4 @@ done items in the category." | |||
| 2164 | 2178 | ||
| 2165 | (provide 'todos) | 2179 | (provide 'todos) |
| 2166 | 2180 | ||
| 2167 | ;; arch-tag: 6fd91be5-776e-4464-a109-da4ea0e4e497 | ||
| 2168 | ;;; todos.el ends here | 2181 | ;;; todos.el ends here |