From 36bf4fa0b3ceef56a89ca657ce82bdc7ea10203f Mon Sep 17 00:00:00 2001 From: Stephen Berman Date: Fri, 21 Aug 2020 22:41:48 +0200 Subject: [PATCH] Fix several todo-mode.el editing bugs (bug#42976) * lisp/calendar/todo-mode.el (todo-insert-item--basic): Ensure the target todo file is in todo-mode. (todo-edit-item--text): When editing a done item comment, prevent clobbering match data on finishing the edit. (todo-edit-item--header): Ensure that decrementing the month of the date header works for intervals greater than a year, and when incrementing or decrementing the month crosses one or more years, adjust the year as needed. (todo-read-category): If we're outside of todo-mode and there is a current todo file, use it; otherwise, use the default todo file. * test/lisp/calendar/todo-mode-tests.el (todo-test-edit-item-date-month): New test. * test/lisp/calendar/todo-mode-resources/todo-test-1.todo: Modify to accommodate new test. --- lisp/calendar/todo-mode.el | 73 ++++++++++++------- .../todo-mode-resources/todo-test-1.todo | 6 +- test/lisp/calendar/todo-mode-tests.el | 46 ++++++++++++ 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el index a49f428a3c8..4f513d33865 100644 --- a/lisp/calendar/todo-mode.el +++ b/lisp/calendar/todo-mode.el @@ -1937,11 +1937,13 @@ their associated keys and their effects." (find-file-noselect file 'nowarn) (set-window-buffer (selected-window) (set-buffer (find-buffer-visiting file))) - ;; If this command was invoked outside of a Todo mode buffer, - ;; the call to todo-current-category above returned nil. If - ;; we just entered Todo mode now, then cat was set to the - ;; file's first category, but if todo-mode was already - ;; enabled, cat did not get set, so we have to do that. + ;; If FILE is not in Todo mode, set it now, which also sets + ;; CAT to the file's first category. + (unless (derived-mode-p 'todo-mode) (todo-mode)) + ;; But if FILE was already in todo-mode and the item insertion + ;; command was invoked outside of a Todo mode buffer, the + ;; above calls to todo-current-category returned nil, so we + ;; have to explicitly set CAT to the current category. (unless cat (setq cat (todo-current-category))) (setq todo-current-todo-file file) @@ -2169,7 +2171,9 @@ the item at point." (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)) + (replace-match (save-match-data + (read-string prompt + (cons (match-string 1) 1))) nil nil nil 1)) (if comment-delete (user-error "There is no comment to delete") @@ -2348,25 +2352,35 @@ made in the number or names of categories." ((or (string= omonth "*") (= mm 13)) (user-error "Cannot increment *")) (t - (let ((mminc (+ mm inc (if (< inc 0) 12 0)))) - ;; Increment or decrement month by INC - ;; modulo 12. - (setq mm (% mminc 12)) - ;; If result is 0, make month December. - (setq mm (if (= mm 0) 12 (abs mm))) + (let* ((mmo mm) + ;; Change by 12 or more months? + (bigincp (>= (abs inc) 12)) + ;; Month number is in range 1..12. + (mminc (+ mm (% inc 12))) + (mm (% (+ mminc 12) 12)) + ;; 12n mod 12 = 0, so 0 is December. + (mm (if (= mm 0) 12 mm)) + ;; Does change in month cross year? + (mmcmp (cond ((< inc 0) (> mm mmo)) + ((> inc 0) (< mm mmo)))) + (yyadjust (if bigincp + (+ (abs (/ inc 12)) + (if mmcmp 1 0)) + 1))) ;; Adjust year if necessary. - (setq year (or (and (cond ((> mminc 12) - (+ yy (/ mminc 12))) - ((< mminc 1) - (- yy (/ mminc 12) 1)) - (t yy)) - (number-to-string yy)) - oyear))) - ;; Return the changed numerical month as - ;; a string or the corresponding month name. - (if omonth - (number-to-string mm) - (aref tma-array (1- mm)))))) + (setq yy (cond ((and (< inc 0) + (or mmcmp bigincp)) + (- yy yyadjust)) + ((and (> inc 0) + (or mmcmp bigincp)) + (+ yy yyadjust)) + (t yy))) + (setq year (number-to-string yy)) + ;; Return the changed numerical month as + ;; a string or the corresponding month name. + (if omonth + (number-to-string mm) + (aref tma-array (1- mm))))))) ;; Since the number corresponding to the arbitrary ;; month name "*" is out of the range of ;; calendar-last-day-of-month, set it to 1 @@ -5923,8 +5937,15 @@ categories from `todo-category-completions-files'." (todo-absolute-file-name (let ((files (mapcar #'todo-short-file-name catfil))) (completing-read (format str cat) files))))))) - ;; Default to the current file. - (unless file0 (setq file0 todo-current-todo-file)) + ;; When called without arg FILE, use fallback todo file. + (unless file0 (setq file0 (or todo-current-todo-file + ;; If we're outside of todo-mode + ;; but there is a current todo + ;; file, use it. + todo-global-current-todo-file + ;; Else, use the default todo file. + (todo-absolute-file-name + todo-default-todo-file)))) ;; First validate only a name passed interactively from ;; todo-add-category, which must be of a nonexistent category. (unless (and (assoc cat categories) (not add)) diff --git a/test/lisp/calendar/todo-mode-resources/todo-test-1.todo b/test/lisp/calendar/todo-mode-resources/todo-test-1.todo index 598d487cad9..557134fd454 100644 --- a/test/lisp/calendar/todo-mode-resources/todo-test-1.todo +++ b/test/lisp/calendar/todo-mode-resources/todo-test-1.todo @@ -1,4 +1,4 @@ -(("testcat1" . [2 0 2 1]) ("testcat2" . [3 0 1 1]) ("testcat3" . [0 0 0 0])) +(("testcat1" . [2 0 2 1]) ("testcat2" . [3 0 1 1]) ("testcat3" . [0 0 0 0]) ("testcat4" . [1 0 0 0])) --==-- testcat1 [May 29, 2017] testcat1 item3 has more than one line @@ -18,3 +18,7 @@ --==-- testcat3 ==--== DONE +--==-- testcat4 +[Jan 1, 2020] testcat4 item1 + +==--== DONE diff --git a/test/lisp/calendar/todo-mode-tests.el b/test/lisp/calendar/todo-mode-tests.el index d65f94d4f31..a19612ee562 100644 --- a/test/lisp/calendar/todo-mode-tests.el +++ b/test/lisp/calendar/todo-mode-tests.el @@ -848,6 +848,52 @@ should display the previously current (or default) todo file." (should (equal todo-current-todo-file todo-test-file-1)) (delete-file (concat file "~"))))) +(ert-deftest todo-test-edit-item-date-month () + "Test incrementing and decrementing the month of an item's date. +If the change in month crosses a year boundary, the year of the +item's date should be adjusted accordingly." + (with-todo-test + (todo-test--show 4) + (let ((current-prefix-arg t) ; For todo-edit-item--header. + (get-date (lambda () + (save-excursion + (todo-date-string-matcher (line-end-position)) + (buffer-substring-no-properties (match-beginning 1) + (match-end 0)))))) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month 0) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month 1) + (should (equal (funcall get-date) "Feb 1, 2020")) + (todo-edit-item--header 'month -1) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month -1) + (should (equal (funcall get-date) "Dec 1, 2019")) + (todo-edit-item--header 'month 1) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month 12) + (should (equal (funcall get-date) "Jan 1, 2021")) + (todo-edit-item--header 'month -12) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month -13) + (should (equal (funcall get-date) "Dec 1, 2018")) + (todo-edit-item--header 'month 7) + (should (equal (funcall get-date) "Jul 1, 2019")) + (todo-edit-item--header 'month 6) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month 23) + (should (equal (funcall get-date) "Dec 1, 2021")) + (todo-edit-item--header 'month -23) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month 24) + (should (equal (funcall get-date) "Jan 1, 2022")) + (todo-edit-item--header 'month -24) + (should (equal (funcall get-date) "Jan 1, 2020")) + (todo-edit-item--header 'month 25) + (should (equal (funcall get-date) "Feb 1, 2022")) + (todo-edit-item--header 'month -25) + (should (equal (funcall get-date) "Jan 1, 2020")) + ))) (provide 'todo-mode-tests) ;;; todo-mode-tests.el ends here -- 2.39.2