diff options
| -rw-r--r-- | lisp/ChangeLog | 27 | ||||
| -rw-r--r-- | lisp/calendar/todos.el | 193 |
2 files changed, 166 insertions, 54 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index ea640f11510..5c4f707d81f 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,30 @@ | |||
| 1 | 2013-02-07 Stephen Berman <stephen.berman@gmx.net> | ||
| 2 | |||
| 3 | * calendar/todos.el: Bug fixes and improvements to item editing | ||
| 4 | and insertion. | ||
| 5 | (todos-check-format): Compare current value of todos-categories | ||
| 6 | with actual categories sexp. | ||
| 7 | (todos-repair-categories-sexp): Add warning to doc string about | ||
| 8 | category order getting restored to list element order. | ||
| 9 | (todos-mode-external-set): When todos-categories is nil, as in | ||
| 10 | Todos Edit mode, set it by reading actual categories sexp. | ||
| 11 | (todos-edit-mode): Make buffer writeable. | ||
| 12 | (todos-done-item-section-p): New function. | ||
| 13 | (todos-insert-item): Use it as part of preventing insertion here | ||
| 14 | in done items section. Move check for display of done items only | ||
| 15 | to just before setting new item's priority, and if cancelled after | ||
| 16 | toggling to todo items, restore display of done items. | ||
| 17 | (todos-edit-multiline-item): Don't base on todos-edit-multiline | ||
| 18 | but just narrow and change mode. | ||
| 19 | (todos-edit-multiline): Don't make indirect buffer but just widen | ||
| 20 | and change mode; also remove overlays. | ||
| 21 | (todos-edit-quit): Restore Todos mode and category display; when | ||
| 22 | quitting multiline item editing, ensure items above edited item | ||
| 23 | are visible in window if possible. | ||
| 24 | (todos-done-item-add-edit-or-delete-comment): If user moved point | ||
| 25 | during editing, make sure it moves back to edited item before | ||
| 26 | returning. | ||
| 27 | |||
| 1 | 2013-02-05 Stephen Berman <stephen.berman@gmx.net> | 28 | 2013-02-05 Stephen Berman <stephen.berman@gmx.net> |
| 2 | 29 | ||
| 3 | * calendar/todos.el (todos-reset-done-separator-string): | 30 | * calendar/todos.el (todos-reset-done-separator-string): |
diff --git a/lisp/calendar/todos.el b/lisp/calendar/todos.el index 479d4c49377..ef05521937f 100644 --- a/lisp/calendar/todos.el +++ b/lisp/calendar/todos.el | |||
| @@ -1094,9 +1094,11 @@ where the invalid formatting was found." | |||
| 1094 | (save-restriction | 1094 | (save-restriction |
| 1095 | (widen) | 1095 | (widen) |
| 1096 | (goto-char (point-min)) | 1096 | (goto-char (point-min)) |
| 1097 | ;; Check for `todos-categories' sexp as the first line | 1097 | (let ((cats (prin1-to-string todos-categories)) |
| 1098 | (let ((cats (prin1-to-string todos-categories))) | 1098 | (sexp (buffer-substring-no-properties (line-beginning-position) |
| 1099 | (unless (looking-at (regexp-quote cats)) | 1099 | (line-end-position)))) |
| 1100 | ;; Check for `todos-categories' sexp as the first line | ||
| 1101 | (unless (string= sexp cats) | ||
| 1100 | (error "Invalid or missing todos-categories sexp"))) | 1102 | (error "Invalid or missing todos-categories sexp"))) |
| 1101 | (forward-line) | 1103 | (forward-line) |
| 1102 | (let ((legit (concat "\\(^" (regexp-quote todos-category-beg) "\\)" | 1104 | (let ((legit (concat "\\(^" (regexp-quote todos-category-beg) "\\)" |
| @@ -1400,7 +1402,11 @@ the file." | |||
| 1400 | (defun todos-repair-categories-sexp () | 1402 | (defun todos-repair-categories-sexp () |
| 1401 | "Repair corrupt Todos categories sexp. | 1403 | "Repair corrupt Todos categories sexp. |
| 1402 | This should only be needed as a consequence of careless manual | 1404 | This should only be needed as a consequence of careless manual |
| 1403 | editing or a bug in todos.el." | 1405 | editing or a bug in todos.el. |
| 1406 | |||
| 1407 | *Warning*: Calling this command restores the category order to | ||
| 1408 | the list element order in the Todos categories sexp, so any order | ||
| 1409 | changes made in Todos Categories mode will have to be made again." | ||
| 1404 | (interactive) | 1410 | (interactive) |
| 1405 | (let ((todos-categories (todos-make-categories-list t))) | 1411 | (let ((todos-categories (todos-make-categories-list t))) |
| 1406 | (todos-update-categories-sexp))) | 1412 | (todos-update-categories-sexp))) |
| @@ -1531,13 +1537,13 @@ The final element is \"*\", indicating an unspecified month.") | |||
| 1531 | (todos-item-start) | 1537 | (todos-item-start) |
| 1532 | (looking-at todos-done-string-start))) | 1538 | (looking-at todos-done-string-start))) |
| 1533 | 1539 | ||
| 1534 | ;; (defun todos-done-item-section-p () | 1540 | (defun todos-done-item-section-p () |
| 1535 | ;; "Return non-nil if point is in category's done items section." | 1541 | "Return non-nil if point is in category's done items section." |
| 1536 | ;; (save-excursion | 1542 | (save-excursion |
| 1537 | ;; (or (re-search-backward (concat "^" (regexp-quote todos-category-done)) | 1543 | (or (re-search-backward (concat "^" (regexp-quote todos-category-done)) |
| 1538 | ;; nil t) | 1544 | nil t) |
| 1539 | ;; (progn (goto-char (point-min)) | 1545 | (progn (goto-char (point-min)) |
| 1540 | ;; (looking-at todos-done-string-start))))) | 1546 | (looking-at todos-done-string-start))))) |
| 1541 | 1547 | ||
| 1542 | (defun todos-prefix-overlay () | 1548 | (defun todos-prefix-overlay () |
| 1543 | "Return this item's prefix overlay." | 1549 | "Return this item's prefix overlay." |
| @@ -2970,7 +2976,15 @@ which is the value of the user option | |||
| 2970 | ;; invocation of `todos-show', since there is then | 2976 | ;; invocation of `todos-show', since there is then |
| 2971 | ;; no buffer visiting the current file. | 2977 | ;; no buffer visiting the current file. |
| 2972 | (find-file-noselect todos-current-todos-file 'nowarn) | 2978 | (find-file-noselect todos-current-todos-file 'nowarn) |
| 2973 | todos-categories))) | 2979 | (or todos-categories |
| 2980 | ;; In Todos Edit mode todos-categories is now nil | ||
| 2981 | ;; since it uses same buffer as Todos mode but | ||
| 2982 | ;; doesn't have the latter's local variables. | ||
| 2983 | (save-excursion | ||
| 2984 | (goto-char (point-min)) | ||
| 2985 | (read (buffer-substring-no-properties | ||
| 2986 | (line-beginning-position) | ||
| 2987 | (line-end-position)))))))) | ||
| 2974 | (set (make-local-variable 'todos-categories) cats))) | 2988 | (set (make-local-variable 'todos-categories) cats))) |
| 2975 | 2989 | ||
| 2976 | (define-derived-mode todos-edit-mode text-mode "Todos-Ed" | 2990 | (define-derived-mode todos-edit-mode text-mode "Todos-Ed" |
| @@ -2978,7 +2992,8 @@ which is the value of the user option | |||
| 2978 | 2992 | ||
| 2979 | \\{todos-edit-mode-map}" | 2993 | \\{todos-edit-mode-map}" |
| 2980 | (todos-modes-set-1) | 2994 | (todos-modes-set-1) |
| 2981 | (todos-mode-external-set)) | 2995 | (todos-mode-external-set) |
| 2996 | (setq buffer-read-only nil)) | ||
| 2982 | 2997 | ||
| 2983 | (put 'todos-categories-mode 'mode-class 'special) | 2998 | (put 'todos-categories-mode 'mode-class 'special) |
| 2984 | 2999 | ||
| @@ -4602,19 +4617,14 @@ the priority is not given by HERE but by prompting." | |||
| 4602 | ;; file's first category. | 4617 | ;; file's first category. |
| 4603 | (set-buffer (find-buffer-visiting file))) | 4618 | (set-buffer (find-buffer-visiting file))) |
| 4604 | (setq todos-current-todos-file file) | 4619 | (setq todos-current-todos-file file) |
| 4605 | ;; If only done items are displayed in category, toggle to | ||
| 4606 | ;; todo items. | ||
| 4607 | (save-excursion | ||
| 4608 | (when (and (goto-char (point-min)) | ||
| 4609 | (looking-at todos-done-string-start)) | ||
| 4610 | (todos-show-done-only))) | ||
| 4611 | (unless todos-global-current-todos-file | 4620 | (unless todos-global-current-todos-file |
| 4612 | (setq todos-global-current-todos-file todos-current-todos-file)) | 4621 | (setq todos-global-current-todos-file todos-current-todos-file)) |
| 4613 | ;; These are not needed here, since they are called in | 4622 | ;; These are not needed here, since they are called in |
| 4614 | ;; todos-set-item-priority. | 4623 | ;; todos-set-item-priority. |
| 4615 | ;; (todos-category-number cat) | 4624 | ;; (todos-category-number cat) |
| 4616 | ;; (todos-category-select) | 4625 | ;; (todos-category-select) |
| 4617 | (let (buffer-read-only) | 4626 | (let ((buffer-read-only nil) |
| 4627 | done-only item-added) | ||
| 4618 | (setq new-item | 4628 | (setq new-item |
| 4619 | ;; Add date, time and diary marking as required. | 4629 | ;; Add date, time and diary marking as required. |
| 4620 | (concat (if (not (and diary (not todos-include-in-diary))) | 4630 | (concat (if (not (and diary (not todos-include-in-diary))) |
| @@ -4633,23 +4643,36 @@ the priority is not given by HERE but by prompting." | |||
| 4633 | (concat "\n" (make-string todos-indent-to-here 32)) | 4643 | (concat "\n" (make-string todos-indent-to-here 32)) |
| 4634 | new-item nil nil 1)) | 4644 | new-item nil nil 1)) |
| 4635 | (if here | 4645 | (if here |
| 4636 | (progn | 4646 | (if (or (todos-done-item-p) (todos-done-item-section-p)) |
| 4647 | (error "Cannot insert item in done items section") | ||
| 4637 | (unless (and todos-mm (equal cat ocat)) | 4648 | (unless (and todos-mm (equal cat ocat)) |
| 4638 | (todos-category-number cat) | 4649 | (todos-category-number cat) |
| 4639 | (todos-category-select) | 4650 | (todos-category-select) |
| 4640 | (goto-char (point-min))) | 4651 | (goto-char (point-min))) |
| 4641 | (todos-insert-with-overlays new-item)) | 4652 | (todos-insert-with-overlays new-item)) |
| 4642 | (unwind-protect | 4653 | (unwind-protect |
| 4643 | (todos-set-item-priority new-item cat t) | 4654 | (progn |
| 4644 | ;; In (at least) two circumstances, point may be at eob | 4655 | ;; If only done items are displayed in category, |
| 4645 | ;; and eob at window-start, so that that the todo items | 4656 | ;; toggle to todo items. |
| 4646 | ;; are out of view: (i) if item is inserted at end of | 4657 | (when (and (goto-char (point-min)) |
| 4647 | ;; category, (ii) if only done items are shown, this is | 4658 | (looking-at todos-done-string-start)) |
| 4648 | ;; (above) programmatically toggled to show todo items, | 4659 | (setq done-only t) |
| 4649 | ;; and user cancels before setting new item's | 4660 | (todos-show-done-only)) |
| 4650 | ;; priority. To make sure the todo items are displayed | 4661 | (todos-set-item-priority new-item cat t) |
| 4651 | ;; in the window, force recentering. | 4662 | (setq item-added t)) |
| 4652 | (recenter))) | 4663 | ;; If user cancels before setting priority, restore |
| 4664 | ;; display. | ||
| 4665 | (unless item-added | ||
| 4666 | (and done-only (todos-show-done-only))) | ||
| 4667 | ;; If todos section is not visible when insertion | ||
| 4668 | ;; command is called (either because only done items | ||
| 4669 | ;; were shown or because category was not in current | ||
| 4670 | ;; buffer), then if item is inserted at end of category, | ||
| 4671 | ;; point is at eob and eob at window-start, so that that | ||
| 4672 | ;; higher priority todo items are out of view. So we | ||
| 4673 | ;; recenter to make sure the todo items are displayed in | ||
| 4674 | ;; the window. | ||
| 4675 | (when item-added (recenter)))) | ||
| 4653 | (todos-update-count 'todo 1) | 4676 | (todos-update-count 'todo 1) |
| 4654 | (if (or diary todos-include-in-diary) (todos-update-count 'diary 1)) | 4677 | (if (or diary todos-include-in-diary) (todos-update-count 'diary 1)) |
| 4655 | (todos-update-categories-sexp)))))) | 4678 | (todos-update-categories-sexp)))))) |
| @@ -4790,29 +4813,77 @@ minibuffer; otherwise, edit it in Todos Edit mode." | |||
| 4790 | (todos-insert-with-overlays new) | 4813 | (todos-insert-with-overlays new) |
| 4791 | (move-to-column item-beg)))))) | 4814 | (move-to-column item-beg)))))) |
| 4792 | 4815 | ||
| 4816 | ;; (defun todos-edit-multiline-item () | ||
| 4817 | ;; "Edit current Todo item in Todos Edit mode. | ||
| 4818 | ;; Use of newlines invokes `todos-indent' to insure compliance with | ||
| 4819 | ;; the format of Diary entries." | ||
| 4820 | ;; (interactive) | ||
| 4821 | ;; (todos-edit-multiline t)) | ||
| 4822 | |||
| 4823 | ;; (defun todos-edit-multiline (&optional item) ;FIXME: not item editing command | ||
| 4824 | ;; "" ;FIXME | ||
| 4825 | ;; (interactive) | ||
| 4826 | ;; (let ((buffer-name todos-edit-buffer)) | ||
| 4827 | ;; (set-window-buffer | ||
| 4828 | ;; (selected-window) | ||
| 4829 | ;; (set-buffer (make-indirect-buffer | ||
| 4830 | ;; (file-name-nondirectory todos-current-todos-file) | ||
| 4831 | ;; buffer-name))) | ||
| 4832 | ;; (if item | ||
| 4833 | ;; (narrow-to-region (todos-item-start) (todos-item-end)) | ||
| 4834 | ;; (widen)) | ||
| 4835 | ;; (todos-edit-mode) | ||
| 4836 | ;; (message "%s" (substitute-command-keys | ||
| 4837 | ;; (concat "Type \\[todos-edit-quit] to check file format " | ||
| 4838 | ;; "validity and return to Todos mode.\n"))))) | ||
| 4839 | |||
| 4840 | ;; (defun todos-edit-quit () | ||
| 4841 | ;; "Return from Todos Edit mode to Todos mode. | ||
| 4842 | ;; If the item contains hard line breaks, make sure the following | ||
| 4843 | ;; lines are indented by `todos-indent-to-here' to conform to diary | ||
| 4844 | ;; format. | ||
| 4845 | |||
| 4846 | ;; If the whole file was in Todos Edit mode, check before returning | ||
| 4847 | ;; whether the file is still a valid Todos file and if so, also | ||
| 4848 | ;; recalculate the Todos categories sexp, in case changes were made | ||
| 4849 | ;; in the number or names of categories." | ||
| 4850 | ;; (interactive) | ||
| 4851 | ;; (if (eq (buffer-size) (- (point-max) (point-min))) | ||
| 4852 | ;; (when (todos-check-format) | ||
| 4853 | ;; (todos-repair-categories-sexp)) | ||
| 4854 | ;; ;; Ensure lines following hard newlines are indented. | ||
| 4855 | ;; (let ((item (replace-regexp-in-string | ||
| 4856 | ;; "\\(\n\\)[^[:blank:]]" | ||
| 4857 | ;; (concat "\n" (make-string todos-indent-to-here 32)) | ||
| 4858 | ;; (buffer-string) nil nil 1))) | ||
| 4859 | ;; (delete-region (point-min) (point-max)) | ||
| 4860 | ;; (insert item))) | ||
| 4861 | ;; (kill-buffer) | ||
| 4862 | ;; ;; In case next buffer is not the one holding todos-current-todos-file. | ||
| 4863 | ;; (todos-show)) | ||
| 4864 | |||
| 4793 | (defun todos-edit-multiline-item () | 4865 | (defun todos-edit-multiline-item () |
| 4794 | "Edit current Todo item in Todos Edit mode. | 4866 | "Edit current Todo item in Todos Edit mode. |
| 4795 | Use of newlines invokes `todos-indent' to insure compliance with | 4867 | Use of newlines invokes `todos-indent' to insure compliance with |
| 4796 | the format of Diary entries." | 4868 | the format of Diary entries." |
| 4797 | (interactive) | 4869 | (interactive) |
| 4798 | (todos-edit-multiline t)) | 4870 | (narrow-to-region (todos-item-start) (todos-item-end)) |
| 4799 | 4871 | (rename-buffer todos-edit-buffer) | |
| 4800 | (defun todos-edit-multiline (&optional item) | 4872 | (todos-edit-mode) |
| 4801 | "" | 4873 | (message "%s" (substitute-command-keys |
| 4874 | (concat "Type \\[todos-edit-quit] " | ||
| 4875 | "to return to Todos mode.\n")))) | ||
| 4876 | |||
| 4877 | (defun todos-edit-multiline (&optional item) ;FIXME: not item editing command | ||
| 4878 | "" ;FIXME | ||
| 4802 | (interactive) | 4879 | (interactive) |
| 4803 | (let ((buffer-name todos-edit-buffer)) | 4880 | (widen) |
| 4804 | (set-window-buffer | 4881 | (rename-buffer todos-edit-buffer) |
| 4805 | (selected-window) | 4882 | (todos-edit-mode) |
| 4806 | (set-buffer (make-indirect-buffer | 4883 | (remove-overlays) ; nil nil 'before-string) |
| 4807 | (file-name-nondirectory todos-current-todos-file) | 4884 | (message "%s" (substitute-command-keys |
| 4808 | buffer-name))) | 4885 | (concat "Type \\[todos-edit-quit] to check file format " |
| 4809 | (if item | 4886 | "validity and return to Todos mode.\n")))) |
| 4810 | (narrow-to-region (todos-item-start) (todos-item-end)) | ||
| 4811 | (widen)) | ||
| 4812 | (todos-edit-mode) | ||
| 4813 | (message "%s" (substitute-command-keys | ||
| 4814 | (concat "Type \\[todos-edit-quit] to check file format " | ||
| 4815 | "validity and return to Todos mode.\n"))))) | ||
| 4816 | 4887 | ||
| 4817 | (defun todos-edit-quit () | 4888 | (defun todos-edit-quit () |
| 4818 | "Return from Todos Edit mode to Todos mode. | 4889 | "Return from Todos Edit mode to Todos mode. |
| @@ -4827,17 +4898,28 @@ in the number or names of categories." | |||
| 4827 | (interactive) | 4898 | (interactive) |
| 4828 | (if (eq (buffer-size) (- (point-max) (point-min))) | 4899 | (if (eq (buffer-size) (- (point-max) (point-min))) |
| 4829 | (when (todos-check-format) | 4900 | (when (todos-check-format) |
| 4830 | (todos-repair-categories-sexp)) | 4901 | ;; FIXME: separate out sexp check? |
| 4902 | ;; If manual editing makes e.g. item counts change, have to | ||
| 4903 | ;; call this to update todos-categories, but it restores | ||
| 4904 | ;; category order to list order. | ||
| 4905 | ;; (todos-repair-categories-sexp) | ||
| 4906 | ;; Compare (todos-make-categories-list t) with sexp and if | ||
| 4907 | ;; different ask (todos-update-categories-sexp) ? | ||
| 4908 | (todos-mode) | ||
| 4909 | (todos-category-select)) | ||
| 4831 | ;; Ensure lines following hard newlines are indented. | 4910 | ;; Ensure lines following hard newlines are indented. |
| 4832 | (let ((item (replace-regexp-in-string | 4911 | (let ((beg (save-excursion (todos-item-start))) |
| 4912 | (item (replace-regexp-in-string | ||
| 4833 | "\\(\n\\)[^[:blank:]]" | 4913 | "\\(\n\\)[^[:blank:]]" |
| 4834 | (concat "\n" (make-string todos-indent-to-here 32)) | 4914 | (concat "\n" (make-string todos-indent-to-here 32)) |
| 4835 | (buffer-string) nil nil 1))) | 4915 | (buffer-string) nil nil 1))) |
| 4836 | (delete-region (point-min) (point-max)) | 4916 | (delete-region (point-min) (point-max)) |
| 4837 | (insert item))) | 4917 | (insert item) |
| 4838 | (kill-buffer) | 4918 | (todos-mode) |
| 4839 | ;; In case next buffer is not the one holding todos-current-todos-file. | 4919 | (todos-category-select) |
| 4840 | (todos-show)) | 4920 | (goto-char (point-min)) |
| 4921 | (goto-char beg) | ||
| 4922 | (recenter)))) | ||
| 4841 | 4923 | ||
| 4842 | (defun todos-edit-item-header-1 (what &optional inc) | 4924 | (defun todos-edit-item-header-1 (what &optional inc) |
| 4843 | "Function underlying commands to edit item date/time header. | 4925 | "Function underlying commands to edit item date/time header. |
| @@ -5565,6 +5647,7 @@ With prefix ARG delete an existing comment." | |||
| 5565 | (interactive "P") | 5647 | (interactive "P") |
| 5566 | (when (todos-done-item-p) | 5648 | (when (todos-done-item-p) |
| 5567 | (let ((item (todos-item-string)) | 5649 | (let ((item (todos-item-string)) |
| 5650 | (opoint (point)) | ||
| 5568 | (end (save-excursion (todos-item-end))) | 5651 | (end (save-excursion (todos-item-end))) |
| 5569 | comment buffer-read-only) | 5652 | comment buffer-read-only) |
| 5570 | (save-excursion | 5653 | (save-excursion |
| @@ -5579,6 +5662,8 @@ With prefix ARG delete an existing comment." | |||
| 5579 | (cons (match-string 1) 1))) | 5662 | (cons (match-string 1) 1))) |
| 5580 | (replace-match comment nil nil nil 1)) | 5663 | (replace-match comment nil nil nil 1)) |
| 5581 | (setq comment (read-string "Enter a comment: ")) | 5664 | (setq comment (read-string "Enter a comment: ")) |
| 5665 | ;; If user moved point during editing, make sure it moves back. | ||
| 5666 | (goto-char opoint) | ||
| 5582 | (todos-item-end) | 5667 | (todos-item-end) |
| 5583 | (insert " [" todos-comment-string ": " comment "]")))))) | 5668 | (insert " [" todos-comment-string ": " comment "]")))))) |
| 5584 | 5669 | ||