Fix several todo-mode.el item editing bugs (bug#63811)

* lisp/calendar/todo-mode.el (todo-insert-item--basic): With
insertion type 'here', ensure item is inserted on the todo-mode
line where the command was invoked.
(todo-edit-item--cat, todo-edit-item--pos): New variables.
(todo-edit-item--text): Restrict the scope of nil-valued
buffer-read-only to the functions that change buffer text.  If
user moved point while editing a single-line todo item or a done
item comment, or while inserting a done item comment, restore
point, and for comments, make sure the done items section is
displayed.  For multiline items, set the new variables so
todo-edit-quit can use them.
(todo-edit-quit): Use the values of the new variables to restore
point in the todo-mode buffer if it had been moved while editing.
(todo-edit-item--header): Avoid clobbering match data when editing
a todo item header.
This commit is contained in:
Stephen Berman 2023-05-31 16:15:48 +02:00
parent ed4cd3eddf
commit 83b22139e4

View file

@ -1985,7 +1985,13 @@ their associated keys and their effects."
(setq done-only t) (setq done-only t)
(todo-toggle-view-done-only)) (todo-toggle-view-done-only))
(if here (if here
(todo-insert-with-overlays new-item) (progn
;; Ensure item is inserted where command was invoked.
(unless (= (point) opoint)
(todo-category-number ocat)
(todo-category-select)
(goto-char opoint))
(todo-insert-with-overlays new-item))
(todo-set-item-priority new-item cat t)) (todo-set-item-priority new-item cat t))
(setq item-added t)) (setq item-added t))
;; If user cancels before setting priority, restore ;; If user cancels before setting priority, restore
@ -2119,6 +2125,9 @@ the item at point."
((or marked (todo-item-string)) ((or marked (todo-item-string))
(todo-edit-item--next-key 'todo arg))))) (todo-edit-item--next-key 'todo arg)))))
(defvar todo-edit-item--cat nil)
(defvar todo-edit-item--pos nil)
(defun todo-edit-item--text (&optional arg) (defun todo-edit-item--text (&optional arg)
"Function providing the text editing facilities of `todo-edit-item'." "Function providing the text editing facilities of `todo-edit-item'."
(let ((full-item (todo-item-string))) (let ((full-item (todo-item-string)))
@ -2127,6 +2136,7 @@ the item at point."
;; 1+ signals an error, so just make this a noop. ;; 1+ signals an error, so just make this a noop.
(when full-item (when full-item
(let* ((opoint (point)) (let* ((opoint (point))
(ocat (todo-current-category))
(start (todo-item-start)) (start (todo-item-start))
(end (save-excursion (todo-item-end))) (end (save-excursion (todo-item-end)))
(item-beg (progn (item-beg (progn
@ -2151,8 +2161,7 @@ the item at point."
(concat " \\[" (regexp-quote todo-comment-string) (concat " \\[" (regexp-quote todo-comment-string)
": \\([^]]+\\)\\]") ": \\([^]]+\\)\\]")
end t))) end t)))
(prompt (if comment "Edit comment: " "Enter a comment: ")) (prompt (if comment "Edit comment: " "Enter a comment: ")))
(buffer-read-only nil))
;; When there are marked items, user can invoke todo-edit-item ;; When there are marked items, user can invoke todo-edit-item
;; even if point is not on an item, but text editing only ;; even if point is not on an item, but text editing only
;; applies to the item at point. ;; applies to the item at point.
@ -2170,22 +2179,43 @@ the item at point."
end t) end t)
(if comment-delete (if comment-delete
(when (todo-y-or-n-p "Delete comment? ") (when (todo-y-or-n-p "Delete comment? ")
(delete-region (match-beginning 0) (match-end 0))) (let ((buffer-read-only nil))
(delete-region (match-beginning 0) (match-end 0))))
(let ((buffer-read-only nil))
(replace-match (save-match-data (replace-match (save-match-data
(read-string prompt (prog1 (let ((buffer-read-only t))
(read-string
prompt
(cons (match-string 1) 1))) (cons (match-string 1) 1)))
nil nil nil 1)) ;; If user moved point while editing
;; a comment, restore it and ensure
;; done items section is displayed.
(unless (= (point) opoint)
(todo-category-number ocat)
(let ((todo-show-with-done t))
(todo-category-select)
(goto-char opoint)))))
nil nil nil 1)))
(if comment-delete (if comment-delete
(user-error "There is no comment to delete") (user-error "There is no comment to delete")
(let ((buffer-read-only nil))
(insert " [" todo-comment-string ": " (insert " [" todo-comment-string ": "
(prog1 (read-string prompt) (prog1 (let ((buffer-read-only t))
;; If user moved point during editing, (read-string prompt))
;; make sure it moves back. ;; If user moved point while inserting a
(goto-char opoint) ;; comment, restore it and ensure done items
;; section is displayed.
(unless (= (point) opoint)
(todo-category-number ocat)
(let ((todo-show-with-done t))
(todo-category-select)
(goto-char opoint)))
(todo-item-end)) (todo-item-end))
"]"))))) "]"))))))
(multiline (multiline
(let ((buf todo-edit-buffer)) (let ((buf todo-edit-buffer))
(setq todo-edit-item--cat ocat)
(setq todo-edit-item--pos opoint)
(set-window-buffer (selected-window) (set-window-buffer (selected-window)
(set-buffer (make-indirect-buffer (set-buffer (make-indirect-buffer
(buffer-name) buf))) (buffer-name) buf)))
@ -2208,10 +2238,14 @@ the item at point."
;; Ensure lines following hard newlines are indented. ;; Ensure lines following hard newlines are indented.
(setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]" (setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
"\n\t" new nil nil 1)) "\n\t" new nil nil 1))
;; If user moved point during editing, make sure it moves back. ;; If user moved point while editing item, restore it.
(goto-char opoint) (unless (= (point) opoint)
(todo-category-number ocat)
(todo-category-select)
(goto-char opoint))
(let ((buffer-read-only nil))
(todo-remove-item) (todo-remove-item)
(todo-insert-with-overlays new) (todo-insert-with-overlays new))
(move-to-column item-beg))))))))) (move-to-column item-beg)))))))))
(defun todo-edit-quit () (defun todo-edit-quit ()
@ -2243,6 +2277,9 @@ made in the number or names of categories."
(kill-buffer) (kill-buffer)
(unless (eq (current-buffer) buf) (unless (eq (current-buffer) buf)
(set-window-buffer (selected-window) (set-buffer buf))) (set-window-buffer (selected-window) (set-buffer buf)))
(todo-category-number todo-edit-item--cat)
(todo-category-select)
(goto-char todo-edit-item--pos)
(if transient-mark-mode (deactivate-mark))) (if transient-mark-mode (deactivate-mark)))
;; We got here via `F e'. ;; We got here via `F e'.
(when (todo-check-format) (when (todo-check-format)
@ -2315,17 +2352,18 @@ made in the number or names of categories."
;; If there are marked items, use only the first to set ;; If there are marked items, use only the first to set
;; header changes, and apply these to all marked items. ;; header changes, and apply these to all marked items.
(when first (when first
(save-match-data
(cond (cond
((eq what 'date) ((eq what 'date)
(setq ndate (todo-read-date))) (setq ndate (todo-read-date)))
((eq what 'calendar) ((eq what 'calendar)
(setq ndate (save-match-data (todo-set-date-from-calendar)))) (setq ndate (todo-set-date-from-calendar)))
((eq what 'today) ((eq what 'today)
(setq ndate (calendar-date-string (calendar-current-date) t t))) (setq ndate (calendar-date-string (calendar-current-date) t t)))
((eq what 'dayname) ((eq what 'dayname)
(setq ndate (todo-read-dayname))) (setq ndate (todo-read-dayname)))
((eq what 'time) ((eq what 'time)
(setq ntime (save-match-data (todo-read-time))) (setq ntime (todo-read-time))
(when (> (length ntime) 0) (when (> (length ntime) 0)
(setq ntime (concat " " ntime)))) (setq ntime (concat " " ntime))))
;; When date string consists only of a day name, ;; When date string consists only of a day name,
@ -2425,7 +2463,7 @@ made in the number or names of categories."
(setq month (number-to-string adjmm)) (setq month (number-to-string adjmm))
(setq monthname (aref tma-array (1- adjmm)))) (setq monthname (aref tma-array (1- adjmm))))
;; Return changed numerical day as a string. ;; Return changed numerical day as a string.
(number-to-string (nth 1 date))))))))) (number-to-string (nth 1 date))))))))))
(unless odayname (unless odayname
;; If year, month or day date string components were ;; If year, month or day date string components were
;; changed, rebuild the date string. ;; changed, rebuild the date string.