summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lisp/ChangeLog53
-rw-r--r--lisp/calendar/todo-mode.el693
2 files changed, 328 insertions, 418 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 7af62b0ee41..816c25cf716 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,56 @@
+2014-05-02 Stephen Berman <stephen.berman@gmx.net>
+
+ * 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.
+
2014-05-02 Leo Liu <sdl.web@gmail.com>
* emacs-lisp/cl-macs.el (cl-deftype): Fix indentation.
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)