summaryrefslogtreecommitdiff
path: root/lisp/calendar/todo-mode.el
diff options
context:
space:
mode:
authorStephen Berman <stephen.berman@gmx.net>2014-05-02 15:58:23 +0200
committerStephen Berman <stephen.berman@gmx.net>2014-05-02 15:58:23 +0200
commitaed4b12d6a24698e87e48e3e677c00451fadc430 (patch)
tree42ba3cac96b2962cc54e4ef35ddab65f01ae6dbb /lisp/calendar/todo-mode.el
parenta419eaa751695009cc4048b0efd97ad67bb84614 (diff)
downloademacs-aed4b12d6a24698e87e48e3e677c00451fadc430.tar.gz
Extend todo item insertion UI to item editing.
* calendar/todo-mode.el: Reimplement item editing to have the same basic user interface as item insertion, and make small UI and larger internal improvements to the latter. (todo-insert-item): Add reference to the Todo mode user manual to the documentation string. (todo-insert-item--basic): Rename from todo-basic-insert-item and adjust all callers. Change signature to combine diary and nonmarking arguments. Incorporate functionality of deleted item copying command and add error checking. Remove detailed descriptions of the arguments from the documentation string, since this is treated in the Todo mode user manual. (todo-copy-item, todo-edit-multiline-item) (todo-edit-done-item-comment, todo-edit-item-header) (todo-edit-item-time, todo-edit-item-date-from-calendar) (todo-edit-item-date-to-today, todo-edit-item-date-day-name) (todo-edit-item-date-year, todo-edit-item-date-month) (todo-edit-item-date-day, todo-edit-item-diary-nonmarking): Remove. (todo-edit-item): Reimplement as wrapper command for todo-edit-item--next-key and make it distinguish done and not done todo items. (todo-edit-item--text): New function, replacing old command todo-edit-item and incorporating deleted commands todo-edit-multiline-item and todo-edit-done-item-comment. (todo-edit-item--header): Rename from todo-basic-edit-item-header. Use only numeric value of prefix argument. Remove detailed descriptions of the arguments from the documentation string, since this is treated in the Todo mode user manual. (todo-edit-item--diary-inclusion): New function, replacing old command todo-edit-item-diary-inclusion and incorporating and fixing functionality of deleted command todo-edit-item-diary-nonmarking, making sure to remove todo-nondiary-marker when adding diary-nonmarking-symbol. (todo-edit-category-diary-inclusion): Make sure to delete diary-nonmarking-symbol when adding todo-nondiary-marker. (todo-edit-category-diary-nonmarking): Fix indentation. (todo-insert-item--parameters): Group diary and nonmarking parameters together. (todo-insert-item--apply-args): Adjust to signature of todo-insert-item--basic and incorporate copy parameter. Make small code improvements. (todo-insert-item--next-param): Improve prompt and adjust it to new parameter grouping. Remove obsolete code. (todo-edit-item--param-key-alist) (todo-edit-item--date-param-key-alist) (todo-edit-done-item--param-key-alist): New defconsts. (todo-edit-item--prompt): New variable. (todo-edit-item--next-key): New function. (todo-key-bindings-t): Bind "e" to todo-edit-item. Remove bindings of deleted commands.
Diffstat (limited to 'lisp/calendar/todo-mode.el')
-rw-r--r--lisp/calendar/todo-mode.el693
1 files changed, 275 insertions, 418 deletions
diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index 09cca201c3c..df9f379a185 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -787,7 +787,7 @@ corresponding todo file, displaying the corresponding category."
(kill-buffer)
(keyboard-quit)))))
(save-excursion (todo-category-select))
- (when add-item (todo-basic-insert-item)))
+ (when add-item (todo-insert-item--basic)))
(setq todo-show-first show-first)
(add-to-list 'todo-visited file)))))
@@ -947,7 +947,7 @@ Categories mode."
(todo-category-number category)
(todo-category-select)
(goto-char (point-min))
- (when add-item (todo-basic-insert-item))))))
+ (when add-item (todo-insert-item--basic))))))
(defun todo-next-item (&optional count)
"Move point down to the beginning of the next item.
@@ -1267,7 +1267,7 @@ return the new category number."
(setq todo-category-number num)
(todo-category-select)
(when todo-add-item-if-new-category
- (todo-basic-insert-item)))
+ (todo-insert-item--basic)))
num))))
(defun todo-rename-category ()
@@ -1752,7 +1752,8 @@ marking of the next N items."
(defvar todo-insert-item--parameters)
(defun todo-insert-item (&optional arg)
- "Insert a new todo item into a category.
+ "Choose an item insertion operation and carry it out.
+This inserts a new todo item into a category.
With no prefix argument ARG, add the item to the current
category; with one prefix argument (`C-u'), prompt for a category
@@ -1766,117 +1767,31 @@ There are a number of item insertion parameters which can be
combined by entering specific keys to produce different insertion
commands. After entering each key, a message shows which have
already been entered and which remain available. See
-`todo-basic-insert-item' for details of the parameters and their
-effects."
+`(todo-mode) Inserting New Items' for details of the parameters,
+their associated keys and their effects."
(interactive "P")
(setq todo-insert-item--keys-so-far "i")
(todo-insert-item--next-param nil (list arg) todo-insert-item--parameters))
-(defun todo-basic-insert-item (&optional arg diary nonmarking date-type time
- region-or-here)
- "Insert a new todo item into a category.
-This is the function from which the generated Todo mode item
-insertion commands derive.
-
-The generated commands have mnemonic key bindings based on the
-arguments' values and their order in the command's argument list,
-as follows: (1) for DIARY `d', (2) for NONMARKING `k', (3) for
-DATE-TYPE either `c' for calendar or `d' for date or `n' for
-weekday name, (4) for TIME `t', (5) for REGION-OR-HERE either `r'
-for region or `h' for here. Sequences of these keys are appended
-to the insertion prefix key `i'. Keys that allow a following
-key (i.e., any but `r' or `h') must be doubled when used finally.
-For example, the command bound to the key sequence `i y h' will
-insert a new item with today's date, marked according to the
-DIARY argument described below, and with priority according to
-the HERE argument; `i y y' does the same except that the priority
-is not given by HERE but by prompting.
-
-In command invocations, ARG is passed as a prefix argument as
-follows. With no prefix argument, add the item to the current
-category; with one prefix argument (`C-u'), prompt for a category
-from the current todo file; with two prefix arguments (`C-u C-u'),
-first prompt for a todo file, then a category in that file. If
-a non-existing category is entered, ask whether to add it to the
-todo file; if answered affirmatively, add the category and
-insert the item there.
-
-The remaining arguments are set or left nil by the generated item
-insertion commands; their meanings are described in the follows
-paragraphs.
-
-When argument DIARY is non-nil, this overrides the intent of the
-user option `todo-include-in-diary' for this item: if
-`todo-include-in-diary' is nil, include the item in the Fancy
-Diary display, and if it is non-nil, exclude the item from the
-Fancy Diary display. When DIARY is nil, `todo-include-in-diary'
-has its intended effect.
-
-When the item is included in the Fancy Diary display and the
-argument NONMARKING is non-nil, this overrides the intent of the
-user option `todo-diary-nonmarking' for this item: if
-`todo-diary-nonmarking' is nil, append `diary-nonmarking-symbol'
-to the item, and if it is non-nil, omit `diary-nonmarking-symbol'.
-
-The argument DATE-TYPE determines the content of the item's
-mandatory date header string and how it is added:
-- If DATE-TYPE is the symbol `calendar', the Calendar pops up and
- when the user puts the cursor on a date and hits RET, that
- date, in the format set by `calendar-date-display-form',
- becomes the date in the header.
-- If DATE-TYPE is a string matching the regexp
- `todo-date-pattern', that string becomes the date in the
- header. This case is for the command
- `todo-insert-item-from-calendar' which is called from the
- Calendar.
-- If DATE-TYPE is the symbol `date', the header contains the date
- in the format set by `calendar-date-display-form', with year,
- month and day individually prompted for (month with tab
- completion).
-- If DATE-TYPE is the symbol `dayname' the header contains a
- weekday name instead of a date, prompted for with tab
- completion.
-- If DATE-TYPE has any other value (including nil or none) the
- header contains the current date (in the format set by
- `calendar-date-display-form').
-
-With non-nil argument TIME prompt for a time string, which must
-match `diary-time-regexp'. Typing `<return>' at the prompt
-returns the current time, if the user option
-`todo-always-add-time-string' is non-nil, otherwise the empty
-string (i.e., no time string). If TIME is absent or nil, add or
-omit the current time string according as
-`todo-always-add-time-string' is non-nil or nil, respectively.
-
-The argument REGION-OR-HERE determines the source and location of
-the new item:
-- If the REGION-OR-HERE is the symbol `here', prompt for the text of
- the new item and, if the command was invoked with point in the todo
- items section of the current category, give the new item the
- priority of the item at point, lowering the latter's priority and
- the priority of the remaining items. If point is in the done items
- section of the category, insert the new item as the first todo item
- in the category. Likewise, if the command with `here' is invoked
- outside of the current category, jump to the chosen category and
- insert the new item as the first item in the category.
-- If REGION-OR-HERE is the symbol `region', use the region of the
- current buffer as the text of the new item, depending on the
- value of user option `todo-use-only-highlighted-region': if
- this is non-nil, then use the region only when it is
- highlighted; otherwise, use the region regardless of
- highlighting. An error is signalled if there is no region in
- the current buffer. Prompt for the item's priority in the
- category (an integer between 1 and one more than the number of
- items in the category), and insert the item accordingly.
-- If REGION-OR-HERE has any other value (in particular, nil or
- none), prompt for the text and the item's priority, and insert
- the item accordingly."
+(defun todo-insert-item--basic (&optional arg diary-type date-type time where)
+ "Function implementing the core of `todo-insert-item'."
;; If invoked outside of Todo mode and there is not yet any Todo
;; file, initialize one.
(if (null (funcall todo-files-function))
(todo-show)
- (let ((region (eq region-or-here 'region))
- (here (eq region-or-here 'here)))
+ (let ((copy (eq where 'copy))
+ (region (eq where 'region))
+ (here (eq where 'here))
+ diary-item)
+ (when copy
+ (cond
+ ((not (eq major-mode 'todo-mode))
+ (user-error "You must be in Todo mode to copy a todo item"))
+ ((todo-done-item-p)
+ (user-error "You cannot copy a done item as a new todo item"))
+ ((looking-at "^$")
+ (user-error "Point must be on a todo item to copy it")))
+ (setq diary-item (todo-diary-item-p)))
(when region
(let (use-empty-active-region)
(unless (and todo-use-only-highlighted-region (use-region-p))
@@ -1899,10 +1814,10 @@ the new item:
todo-default-todo-file))))))
(cat (car cat+file))
(file (cdr cat+file))
- (new-item (if region
- (buffer-substring-no-properties
- (region-beginning) (region-end))
- (read-from-minibuffer "Todo item: ")))
+ (new-item (cond (copy (todo-item-string))
+ (region (buffer-substring-no-properties
+ (region-beginning) (region-end)))
+ (t (read-from-minibuffer "Todo item: "))))
(date-string (cond
((eq date-type 'date)
(todo-read-date))
@@ -1941,22 +1856,26 @@ the new item:
(let ((buffer-read-only nil)
(called-from-outside (not (and todo-mm (equal cat ocat))))
done-only item-added)
- (setq new-item
- ;; Add date, time and diary marking as required.
- (concat (if (not (and diary (not todo-include-in-diary)))
- todo-nondiary-start
- (when (and nonmarking (not todo-diary-nonmarking))
- diary-nonmarking-symbol))
- date-string (when (and time-string ; Can be empty.
- (not (zerop (length
- time-string))))
- (concat " " time-string))
- (when (not (and diary (not todo-include-in-diary)))
- todo-nondiary-end)
- " " new-item))
- ;; Indent newlines inserted by C-q C-j if nonspace char follows.
- (setq new-item (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
- "\n\t" new-item nil nil 1))
+ (unless copy
+ (setq new-item
+ ;; Add date, time and diary marking as required.
+ (concat (if (not (and diary-type
+ (not todo-include-in-diary)))
+ todo-nondiary-start
+ (when (and (eq diary-type 'nonmarking)
+ (not todo-diary-nonmarking))
+ diary-nonmarking-symbol))
+ date-string (when (and time-string ; Can be empty.
+ (not (zerop (length
+ time-string))))
+ (concat " " time-string))
+ (when (not (and diary-type
+ (not todo-include-in-diary)))
+ todo-nondiary-end)
+ " " new-item))
+ ;; Indent newlines inserted by C-q C-j if nonspace char follows.
+ (setq new-item (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
+ "\n\t" new-item nil nil 1)))
(unwind-protect
(progn
;; Make sure the correct category is selected. There
@@ -2010,7 +1929,8 @@ the new item:
;; items are displayed in the window.
(when item-added (recenter)))
(todo-update-count 'todo 1)
- (if (or diary todo-include-in-diary) (todo-update-count 'diary 1))
+ (when (or diary-item diary-type todo-include-in-diary)
+ (todo-update-count 'diary 1))
(todo-update-categories-sexp))))))
(defun todo-set-date-from-calendar ()
@@ -2054,21 +1974,10 @@ prompt for a todo file and then for a category in it."
(setq todo-date-from-calendar
(calendar-date-string (calendar-cursor-to-date t) t t))
(calendar-exit)
- (todo-basic-insert-item arg nil nil todo-date-from-calendar))
+ (todo-insert-item--basic arg nil nil todo-date-from-calendar))
(define-key calendar-mode-map "it" 'todo-insert-item-from-calendar)
-(defun todo-copy-item ()
- "Copy item at point and insert the copy as a new item."
- (interactive)
- (unless (or (todo-done-item-p) (looking-at "^$"))
- (let ((copy (todo-item-string))
- (diary-item (todo-diary-item-p)))
- (todo-set-item-priority copy (todo-current-category) t)
- (todo-update-count 'todo 1)
- (when diary-item (todo-update-count 'diary 1))
- (todo-update-categories-sexp))))
-
(defun todo-delete-item ()
"Delete at least one item in this category.
If there are marked items, delete all of these; otherwise, delete
@@ -2115,64 +2024,91 @@ the item at point."
(todo-prefix-overlays)))
(if ov (delete-overlay ov)))))
-(defun todo-edit-item (&optional arg)
- "Edit the todo item at point.
-With non-nil prefix argument ARG, include the item's date/time
-header, making it also editable; otherwise, include only the item
-content.
+(defvar todo-edit-item--param-key-alist)
+(defvar todo-edit-done-item--param-key-alist)
-If the item consists of only one logical line, edit it in the
-minibuffer; otherwise, edit it in Todo Edit mode."
+(defun todo-edit-item (&optional arg)
+ "Choose an editing operation for the current item and carry it out."
(interactive "P")
- (when (todo-item-string)
- (let* ((opoint (point))
- (start (todo-item-start))
- (item-beg (progn
- (re-search-forward
- (concat todo-date-string-start todo-date-pattern
- "\\( " diary-time-regexp "\\)?"
- (regexp-quote todo-nondiary-end) "?")
- (line-end-position) t)
- (1+ (- (point) start))))
- (header (substring (todo-item-string) 0 item-beg))
- (item (if arg (todo-item-string)
- (substring (todo-item-string) item-beg)))
- (multiline (> (length (split-string item "\n")) 1))
- (buffer-read-only nil))
- (if multiline
- (todo-edit-multiline-item)
- (let ((new (concat (if arg "" header)
- (read-string "Edit: " (if arg
- (cons item item-beg)
- (cons item 0))))))
- (when arg
- (while (not (string-match (concat todo-date-string-start
- todo-date-pattern) new))
- (setq new (read-from-minibuffer
- "Item must start with a date: " new))))
- ;; Ensure lines following hard newlines are indented.
- (setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
- "\n\t" new nil nil 1))
- ;; If user moved point during editing, make sure it moves back.
- (goto-char opoint)
- (todo-remove-item)
- (todo-insert-with-overlays new)
- (move-to-column item-beg))))))
-
-(defun todo-edit-multiline-item ()
- "Edit current todo item in Todo Edit mode.
-Use of newlines invokes `todo-indent' to insure compliance with
-the format of Diary entries."
- (interactive)
- (when (todo-item-string)
- (let ((buf todo-edit-buffer))
- (set-window-buffer (selected-window)
- (set-buffer (make-indirect-buffer (buffer-name) buf)))
- (narrow-to-region (todo-item-start) (todo-item-end))
- (todo-edit-mode)
- (message "%s" (substitute-command-keys
- (concat "Type \\[todo-edit-quit] "
- "to return to Todo mode.\n"))))))
+ (cond ((todo-done-item-p)
+ (todo-edit-item--next-key todo-edit-done-item--param-key-alist))
+ ((todo-item-string)
+ (todo-edit-item--next-key todo-edit-item--param-key-alist arg))))
+
+(defun todo-edit-item--text (&optional arg)
+ "Function providing the text editing facilities of `todo-edit-item'."
+ (let* ((opoint (point))
+ (start (todo-item-start))
+ (end (save-excursion (todo-item-end)))
+ (item-beg (progn
+ (re-search-forward
+ (concat todo-date-string-start todo-date-pattern
+ "\\( " diary-time-regexp "\\)?"
+ (regexp-quote todo-nondiary-end) "?")
+ (line-end-position) t)
+ (1+ (- (point) start))))
+ (include-header (eq arg 'include-header))
+ (comment-edit (eq arg 'comment-edit))
+ (comment-delete (eq arg 'comment-delete))
+ (header-string (substring (todo-item-string) 0 item-beg))
+ (item (if (or include-header comment-edit comment-delete)
+ (todo-item-string)
+ (substring (todo-item-string) item-beg)))
+ (multiline (> (length (split-string item "\n")) 1))
+ (comment (save-excursion
+ (todo-item-start)
+ (re-search-forward
+ (concat " \\[" (regexp-quote todo-comment-string)
+ ": \\([^]]+\\)\\]") end t)))
+ (prompt (if comment "Edit comment: " "Enter a comment: "))
+ (buffer-read-only nil))
+ (cond
+ ((or comment-edit comment-delete)
+ (save-excursion
+ (todo-item-start)
+ (if (re-search-forward (concat " \\[" (regexp-quote todo-comment-string)
+ ": \\([^]]+\\)\\]") end t)
+ (if comment-delete
+ (when (todo-y-or-n-p "Delete comment? ")
+ (delete-region (match-beginning 0) (match-end 0)))
+ (replace-match (read-string prompt (cons (match-string 1) 1))
+ nil nil nil 1))
+ (if comment-delete
+ (user-error "There is no comment to delete")
+ (insert " [" todo-comment-string ": "
+ (prog1 (read-string prompt)
+ ;; If user moved point during editing,
+ ;; make sure it moves back.
+ (goto-char opoint)
+ (todo-item-end))
+ "]")))))
+ ((or multiline (eq arg 'multiline))
+ (let ((buf todo-edit-buffer))
+ (set-window-buffer (selected-window)
+ (set-buffer (make-indirect-buffer (buffer-name) buf)))
+ (narrow-to-region (todo-item-start) (todo-item-end))
+ (todo-edit-mode)
+ (message "%s" (substitute-command-keys
+ (concat "Type \\[todo-edit-quit] "
+ "to return to Todo mode.\n")))))
+ (t
+ (let ((new (concat (if include-header "" header-string)
+ (read-string "Edit: " (if include-header
+ (cons item item-beg)
+ (cons item 0))))))
+ (when include-header
+ (while (not (string-match (concat todo-date-string-start
+ todo-date-pattern) new))
+ (setq new (read-from-minibuffer
+ "Item must start with a date: " new))))
+ ;; Ensure lines following hard newlines are indented.
+ (setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
+ "\n\t" new nil nil 1))
+ ;; If user moved point during editing, make sure it moves back.
+ (goto-char opoint)
+ (todo-remove-item)
+ (todo-insert-with-overlays new)
+ (move-to-column item-beg))))))
(defun todo-edit-quit ()
"Return from Todo Edit mode to Todo mode.
@@ -2225,35 +2161,15 @@ made in the number or names of categories."
(todo-category-select)
(goto-char (point-min))))))
-(defun todo-basic-edit-item-header (what &optional inc)
- "Function underlying commands to edit item date/time header.
-
-The argument WHAT (passed by invoking commands) specifies what
-part of the header to edit; possible values are these symbols:
-`date', to edit the year, month, and day of the date string;
-`time', to edit just the time string; `calendar', to select the
-date from the Calendar; `today', to set the date to today's date;
-`dayname', to set the date string to the name of a day or to
-change the day name; and `year', `month' or `day', to edit only
-these respective parts of the date string (`day' is the number of
-the given day of the month, and `month' is either the name of the
-given month or its number, depending on the value of
-`calendar-date-display-form').
-
-The optional argument INC is a positive or negative integer
-\(passed by invoking commands as a numerical prefix argument)
-that in conjunction with the WHAT values `year', `month' or
-`day', increments or decrements the specified date string
-component by the specified number of suitable units, i.e., years,
-months, or days, with automatic adjustment of the other date
-string components as necessary.
-
-If there are marked items, apply the same edit to all of these;
-otherwise, edit just the item at point."
+(defun todo-edit-item--header (what &optional inc)
+ "Function providing header editing facilities of `todo-edit-item'."
(let* ((cat (todo-current-category))
(marked (assoc cat todo-categories-with-marks))
(first t)
(todo-date-from-calendar t)
+ ;; INC must be an integer, but users could pass it via
+ ;; `todo-edit-item' as e.g. `-' or `C-u'.
+ (inc (prefix-numeric-value inc))
(buffer-read-only nil)
ndate ntime year monthname month day
dayname) ; Needed by calendar-date-display-form.
@@ -2372,7 +2288,8 @@ otherwise, edit just the item at point."
((or (string= omonth "*") (string= omonthname "*"))
(setq dd (+ dd inc))
(if (> dd 31)
- (user-error "A month cannot have more than 31 days")
+ (user-error
+ "A month cannot have more than 31 days")
(number-to-string dd)))
;; Increment or decrement day by INC,
;; adjusting month and year if necessary
@@ -2414,65 +2331,8 @@ otherwise, edit just the item at point."
(todo-forward-item)
(goto-char (point-max))))))))
-(defun todo-edit-item-header ()
- "Interactively edit at least the date of item's date/time header.
-If user option `todo-always-add-time-string' is non-nil, also
-edit item's time string."
- (interactive)
- (todo-basic-edit-item-header 'date)
- (when todo-always-add-time-string
- (todo-edit-item-time)))
-
-(defun todo-edit-item-time ()
- "Interactively edit the time string of item's date/time header."
- (interactive)
- (todo-basic-edit-item-header 'time))
-
-(defun todo-edit-item-date-from-calendar ()
- "Interactively edit item's date using the Calendar."
- (interactive)
- (todo-basic-edit-item-header 'calendar))
-
-(defun todo-edit-item-date-to-today ()
- "Set item's date to today's date."
- (interactive)
- (todo-basic-edit-item-header 'today))
-
-(defun todo-edit-item-date-day-name ()
- "Replace item's date with the name of a day of the week."
- (interactive)
- (todo-basic-edit-item-header 'dayname))
-
-(defun todo-edit-item-date-year (&optional inc)
- "Interactively edit the year of item's date string.
-With prefix argument INC a positive or negative integer,
-increment or decrement the year by INC."
- (interactive "p")
- (todo-basic-edit-item-header 'year inc))
-
-(defun todo-edit-item-date-month (&optional inc)
- "Interactively edit the month of item's date string.
-With prefix argument INC a positive or negative integer,
-increment or decrement the month by INC."
- (interactive "p")
- (todo-basic-edit-item-header 'month inc))
-
-(defun todo-edit-item-date-day (&optional inc)
- "Interactively edit the day of the month of item's date string.
-With prefix argument INC a positive or negative integer,
-increment or decrement the day by INC."
- (interactive "p")
- (todo-basic-edit-item-header 'day inc))
-
-(defun todo-edit-item-diary-inclusion ()
- "Change diary status of one or more todo items in this category.
-That is, insert `todo-nondiary-marker' if the candidate items
-lack this marking; otherwise, remove it.
-
-If there are marked todo items, change the diary status of all
-and only these, otherwise change the diary status of the item at
-point."
- (interactive)
+(defun todo-edit-item--diary-inclusion (&optional nonmarking)
+ "Function providing diary marking facilities of `todo-edit-item'."
(let ((buffer-read-only)
(marked (assoc (todo-current-category)
todo-categories-with-marks)))
@@ -2488,17 +2348,30 @@ point."
(end (save-excursion
(or (todo-time-string-matcher lim)
(todo-date-string-matcher lim)))))
- (if (looking-at (regexp-quote todo-nondiary-start))
- (progn
- (replace-match "")
- (search-forward todo-nondiary-end (1+ end) t)
- (replace-match "")
- (todo-update-count 'diary 1))
- (when end
- (insert todo-nondiary-start)
- (goto-char (1+ end))
- (insert todo-nondiary-end)
- (todo-update-count 'diary -1)))))
+ (if nonmarking
+ (if (looking-at (regexp-quote diary-nonmarking-symbol))
+ (replace-match "")
+ (when (looking-at (regexp-quote todo-nondiary-start))
+ (save-excursion
+ (replace-match "")
+ (search-forward todo-nondiary-end (1+ end) t)
+ (replace-match "")
+ (todo-update-count 'diary 1)))
+ (insert diary-nonmarking-symbol))
+ (if (looking-at (regexp-quote todo-nondiary-start))
+ (progn
+ (replace-match "")
+ (search-forward todo-nondiary-end (1+ end) t)
+ (replace-match "")
+ (todo-update-count 'diary 1))
+ (when end
+ (when (looking-at (regexp-quote diary-nonmarking-symbol))
+ (replace-match "")
+ (setq end (1- end))) ; Since we deleted nonmarking symbol.
+ (insert todo-nondiary-start)
+ (goto-char (1+ end))
+ (insert todo-nondiary-end)
+ (todo-update-count 'diary -1))))))
(unless marked (throw 'stop nil))
(todo-forward-item)))))
(todo-update-categories-sexp)))
@@ -2524,6 +2397,9 @@ items."
(todo-date-string-matcher lim)))))
(if arg
(unless (looking-at (regexp-quote todo-nondiary-start))
+ (when (looking-at (regexp-quote diary-nonmarking-symbol))
+ (replace-match "")
+ (setq end (1- end))) ; Since we deleted nonmarking symbol.
(insert todo-nondiary-start)
(goto-char (1+ end))
(insert todo-nondiary-end))
@@ -2538,33 +2414,6 @@ items."
(- todo-count diary-count))))
(todo-update-categories-sexp)))))
-(defun todo-edit-item-diary-nonmarking ()
- "Change non-marking of one or more diary items in this category.
-That is, insert `diary-nonmarking-symbol' if the candidate items
-lack this marking; otherwise, remove it.
-
-If there are marked todo items, change the non-marking status of
-all and only these, otherwise change the non-marking status of
-the item at point."
- (interactive)
- (let ((buffer-read-only)
- (marked (assoc (todo-current-category)
- todo-categories-with-marks)))
- (catch 'stop
- (save-excursion
- (when marked (goto-char (point-min)))
- (while (not (eobp))
- (if (todo-done-item-p)
- (throw 'stop (message "Done items cannot be edited"))
- (unless (and marked (not (todo-marked-item-p)))
- (todo-item-start)
- (unless (looking-at (regexp-quote todo-nondiary-start))
- (if (looking-at (regexp-quote diary-nonmarking-symbol))
- (replace-match "")
- (insert diary-nonmarking-symbol))))
- (unless marked (throw 'stop nil))
- (todo-forward-item)))))))
-
(defun todo-edit-category-diary-nonmarking (arg)
"Add `diary-nonmarking-symbol' to all diary items in this category.
With prefix ARG, remove `diary-nonmarking-symbol' from all diary
@@ -2574,16 +2423,16 @@ items in this category."
(goto-char (point-min))
(let (buffer-read-only)
(catch 'stop
- (while (not (eobp))
- (if (todo-done-item-p) ; We've gone too far.
- (throw 'stop nil)
- (unless (looking-at (regexp-quote todo-nondiary-start))
- (if arg
- (when (looking-at (regexp-quote diary-nonmarking-symbol))
- (replace-match ""))
- (unless (looking-at (regexp-quote diary-nonmarking-symbol))
- (insert diary-nonmarking-symbol))))
- (todo-forward-item)))))))
+ (while (not (eobp))
+ (if (todo-done-item-p) ; We've gone too far.
+ (throw 'stop nil)
+ (unless (looking-at (regexp-quote todo-nondiary-start))
+ (if arg
+ (when (looking-at (regexp-quote diary-nonmarking-symbol))
+ (replace-match ""))
+ (unless (looking-at (regexp-quote diary-nonmarking-symbol))
+ (insert diary-nonmarking-symbol))))
+ (todo-forward-item)))))))
(defun todo-set-item-priority (&optional item cat new arg)
"Prompt for and set ITEM's priority in CATegory.
@@ -2970,32 +2819,6 @@ visible."
;; When done items are shown, put cursor on first just done item.
(when opoint (goto-char opoint)))))))
-(defun todo-edit-done-item-comment (&optional arg)
- "Add a comment to this done item or edit an existing comment.
-With prefix ARG delete an existing comment."
- (interactive "P")
- (when (todo-done-item-p)
- (let ((item (todo-item-string))
- (opoint (point))
- (end (save-excursion (todo-item-end)))
- comment buffer-read-only)
- (save-excursion
- (todo-item-start)
- (if (re-search-forward (concat " \\["
- (regexp-quote todo-comment-string)
- ": \\([^]]+\\)\\]") end t)
- (if arg
- (when (todo-y-or-n-p "Delete comment? ")
- (delete-region (match-beginning 0) (match-end 0)))
- (setq comment (read-string "Edit comment: "
- (cons (match-string 1) 1)))
- (replace-match comment nil nil nil 1))
- (setq comment (read-string "Enter a comment: "))
- ;; If user moved point during editing, make sure it moves back.
- (goto-char opoint)
- (todo-item-end)
- (insert " [" todo-comment-string ": " comment "]"))))))
-
(defun todo-item-undone ()
"Restore at least one done item to this category's todo section.
Prompt for the new priority. If there are marked items, undo all
@@ -5451,7 +5274,7 @@ of each other."
(forward-line)))))
;; -----------------------------------------------------------------------------
-;;; Utilities for generating item insertion commands and key bindings
+;;; Generating and applying item insertion and editing key sequences
;; -----------------------------------------------------------------------------
;; Thanks to Stefan Monnier for suggesting dynamically generating item
@@ -5462,7 +5285,7 @@ of each other."
;; uses dynamic binding.
(defconst todo-insert-item--parameters
- '((default copy) diary nonmarking (calendar date dayname) time (here region))
+ '((default copy) (diary nonmarking) (calendar date dayname) time (here region))
"List of all item insertion parameters.
Passed by `todo-insert-item' to `todo-insert-item--next-param' to
dynamically create item insertion commands.")
@@ -5527,25 +5350,20 @@ occupied by `nil'."
(list (car (todo-insert-item--argsleft
(todo-insert-item--this-key)
todo-insert-item--argsleft)))))
- (arglist (unless (= 5 (length args))
- (let ((v (make-vector 5 nil)) elt)
+ (arglist (unless (= 4 (length args))
+ (let ((v (make-vector 4 nil)) elt)
(while args
(setq elt (pop args))
- (cond ((eq elt 'diary)
+ (cond ((memq elt '(diary nonmarking))
(aset v 0 elt))
- ((eq elt 'nonmarking)
+ ((memq elt '(calendar date dayname))
(aset v 1 elt))
- ((or (eq elt 'calendar)
- (eq elt 'date)
- (eq elt 'dayname))
- (aset v 2 elt))
((eq elt 'time)
- (aset v 3 elt))
- ((or (eq elt 'here)
- (eq elt 'region))
- (aset v 4 elt))))
+ (aset v 2 elt))
+ ((memq elt '(copy here region))
+ (aset v 3 elt))))
(append v nil)))))
- (apply #'todo-basic-insert-item (nconc arg arglist))))
+ (apply #'todo-insert-item--basic (nconc arg arglist))))
(defun todo-insert-item--next-param (last args argsleft)
"Build item insertion command from LAST, ARGS and ARGSLEFT and call it.
@@ -5554,35 +5372,31 @@ already entered and those still available."
(cl-assert argsleft)
(let* ((map (make-sparse-keymap))
(prompt nil)
- (addprompt (lambda (k name)
- (setq prompt (concat prompt
- (format (concat
- (if (or (eq name 'default)
- (eq name 'calendar)
- (eq name 'here))
- " { " " ")
- "%s=>%s"
- (when (or (eq name 'copy)
- (eq name 'dayname)
- (eq name 'region))
- " }"))
- (propertize k 'face
- 'todo-key-prompt)
- name))))))
+ (addprompt
+ (lambda (k name)
+ (setq prompt
+ (concat prompt
+ (format
+ (concat
+ (if (memq name '(default diary calendar here))
+ " { " " ")
+ "%s=>%s"
+ (when (memq name '(copy nonmarking dayname region))
+ " }"))
+ (propertize k 'face 'todo-key-prompt)
+ name))))))
(setq todo-insert-item--args args)
(setq todo-insert-item--argsleft argsleft)
(when last
- (cond ((eq last 'default)
- (apply #'todo-basic-insert-item (car todo-insert-item--args))
- (setq todo-insert-item--argsleft nil))
- ((eq last 'copy)
- (todo-copy-item)
- (setq todo-insert-item--argsleft nil))
- (t (let ((k (todo-insert-item--keyof last)))
- (funcall addprompt k 'GO!)
- (define-key map (todo-insert-item--keyof last)
- (lambda () (interactive)
- (todo-insert-item--apply-args)))))))
+ (if (memq last '(default copy))
+ (progn
+ (setq todo-insert-item--argsleft nil)
+ (todo-insert-item--apply-args))
+ (let ((k (todo-insert-item--keyof last)))
+ (funcall addprompt k (make-symbol (concat (symbol-name last) ":GO!")))
+ (define-key map (todo-insert-item--keyof last)
+ (lambda () (interactive)
+ (todo-insert-item--apply-args))))))
(while todo-insert-item--argsleft
(let ((x (car todo-insert-item--argsleft)))
(setq todo-insert-item--newargsleft (cdr todo-insert-item--argsleft))
@@ -5594,14 +5408,6 @@ already entered and those still available."
(lambda () (interactive)
(todo-insert-item--apply-args))
(lambda () (interactive)
- (when (equal "k" (todo-insert-item--this-key))
- (unless (string-match "y" todo-insert-item--keys-so-far)
- (when (y-or-n-p (concat "`k' only takes effect with `y';"
- " add `y'? "))
- (setq todo-insert-item--keys-so-far
- (concat todo-insert-item--keys-so-far " y"))
- (setq todo-insert-item--args
- (nconc todo-insert-item--args (list 'diary))))))
(setq todo-insert-item--keys-so-far
(concat todo-insert-item--keys-so-far " "
(todo-insert-item--this-key)))
@@ -5617,11 +5423,74 @@ already entered and those still available."
(todo-insert-item--this-key)
todo-insert-item--argsleft)))))))))
(setq todo-insert-item--argsleft todo-insert-item--newargsleft))
- (when prompt (message "Enter a key (so far `%s'): %s"
+ (when prompt (message "Press a key (so far `%s'): %s"
todo-insert-item--keys-so-far prompt))
(set-transient-map map)
(setq todo-insert-item--argsleft argsleft)))
+(defconst todo-edit-item--param-key-alist
+ '((edit . "e")
+ (header . "h")
+ (multiline . "m")
+ (diary . "y")
+ (nonmarking . "k")
+ (date . "d")
+ (time . "t"))
+ "Alist of item editing parameters and their keys.")
+
+(defconst todo-edit-item--date-param-key-alist
+ '((full . "f")
+ (calendar . "c")
+ (today . "a")
+ (dayname . "n")
+ (year . "y")
+ (month . "m")
+ (daynum . "d"))
+ "Alist of item date editing parameters and their keys.")
+
+(defconst todo-edit-done-item--param-key-alist
+ '((add/edit . "c")
+ (delete . "d"))
+ "Alist of done item comment editing parameters and their keys.")
+
+(defvar todo-edit-item--prompt "Press a key (so far `e'): ")
+
+(defun todo-edit-item--next-key (params &optional arg)
+ (let* ((map (make-sparse-keymap))
+ (p->k (mapconcat (lambda (elt)
+ (format "%s=>%s"
+ (propertize (cdr elt) 'face
+ 'todo-key-prompt)
+ (concat (symbol-name (car elt))
+ (when (memq (car elt)
+ '(add/edit delete))
+ " comment"))))
+ params " "))
+ (this-key (char-to-string
+ (read-key (concat todo-edit-item--prompt p->k))))
+ (this-param (car (rassoc this-key params))))
+ (pcase this-param
+ (`edit (todo-edit-item--text))
+ (`header (todo-edit-item--text 'include-header))
+ (`multiline (todo-edit-item--text 'multiline))
+ (`add/edit (todo-edit-item--text 'comment-edit))
+ (`delete (todo-edit-item--text 'comment-delete))
+ (`diary (todo-edit-item--diary-inclusion))
+ (`nonmarking (todo-edit-item--diary-inclusion 'nonmarking))
+ (`date (let ((todo-edit-item--prompt "Press a key (so far `e d'): "))
+ (todo-edit-item--next-key
+ todo-edit-item--date-param-key-alist arg)))
+ (`full (progn (todo-edit-item--header 'date)
+ (when todo-always-add-time-string
+ (todo-edit-item--header 'time))))
+ (`calendar (todo-edit-item--header 'calendar))
+ (`today (todo-edit-item--header 'today))
+ (`dayname (todo-edit-item--header 'dayname))
+ (`year (todo-edit-item--header 'year arg))
+ (`month (todo-edit-item--header 'month arg))
+ (`daynum (todo-edit-item--header 'day arg))
+ (`time (todo-edit-item--header 'time)))))
+
;; -----------------------------------------------------------------------------
;;; Todo minibuffer utilities
;; -----------------------------------------------------------------------------
@@ -6322,19 +6191,7 @@ Filtered Items mode following todo (not done) items."
("Fym" todo-filter-diary-items-multifile)
("Fxx" todo-filter-regexp-items)
("Fxm" todo-filter-regexp-items-multifile)
- ("ee" todo-edit-item)
- ("em" todo-edit-multiline-item)
- ("edt" todo-edit-item-header)
- ("edc" todo-edit-item-date-from-calendar)
- ("eda" todo-edit-item-date-to-today)
- ("edn" todo-edit-item-date-day-name)
- ("edy" todo-edit-item-date-year)
- ("edm" todo-edit-item-date-month)
- ("edd" todo-edit-item-date-day)
- ("et" todo-edit-item-time)
- ("eyy" todo-edit-item-diary-inclusion)
- ("eyk" todo-edit-item-diary-nonmarking)
- ("ec" todo-edit-done-item-comment)
+ ("e" todo-edit-item)
("d" todo-item-done)
("i" todo-insert-item)
("k" todo-delete-item)