the highest or lowest priority in the category, if you do not
explicitly assign it a priority on invoking @code{todo-insert-item}.
By default, such new items are given highest priority, i.e., inserted
-at the top of the list.
+at the top of the list. In addition, when setting an item's priority
+you can use the minibuffer history to quickly call up the lowest or
+highest priority number in the minibuffer by typing @kbd{M-p} or
+@kbd{M-n}, and you can scroll through all priority numbers for the
+current category with these keys. For example, with the default
+setting of @code{todo-default-priority}, you can insert a new item as
+second to last in the category by typing @kbd{M-p M-p} at the prompt
+for setting the priority.
@item
@code{todo-always-add-time-string} is for including or omitting the
items are pushed down by one. You can also pass the desired priority
as a numeric prefix argument, e.g., @kbd{3 #} gives the item third
highest priority without prompting. (Prefix arguments have no effect
-with @kbd{r} or @kbd{l}.)
+with @kbd{r} or @kbd{l}.) And you can type @kbd{M-p} and @kbd{M-n} in
+the minibuffer to scroll through all priority numbers for the current
+category. If you mistakenly choose the item's current priority, you
+will be prompted to choose a different priority.
@end table
@node Moving and Deleting Items
(save-excursion
(re-search-forward regexp1 nil t)
(match-string-no-properties 1)))))))
- curnum
+ (count 1)
+ (curnum (save-excursion
+ (let ((curstart
+ ;; If point is in done items section or not on an
+ ;; item, use position of first todo item to avoid
+ ;; the while-loop.
+ (or (and (not (todo-done-item-section-p))
+ (todo-item-start))
+ (point-min))))
+ (goto-char (point-min))
+ (while (/= (point) curstart)
+ (setq count (1+ count))
+ (todo-forward-item))
+ count)))
(todo (cond ((or (memq arg '(raise lower))
(eq major-mode 'todo-filtered-items-mode))
(save-excursion
- (let ((curstart (todo-item-start))
- (count 0))
- (goto-char (point-min))
+ (let ((count curnum))
(while (looking-at todo-item-start)
(setq count (1+ count))
- (when (= (point) curstart) (setq curnum count))
(todo-forward-item))
count)))
((eq major-mode 'todo-mode)
((and (eq arg 'raise) (>= curnum 1))
(1- curnum))
((and (eq arg 'lower) (<= curnum maxnum))
- (1+ curnum))))
- candidate)
+ (1+ curnum)))))
+ (and (called-interactively-p 'any)
+ priority ; Check further only if arg or prefix arg was passed.
+ (or (< priority 1) (> priority maxnum))
+ (user-error (format "Priority must be an integer between 1 and %d"
+ maxnum)))
(unless (and priority
+ (/= priority curnum)
(or (and (eq arg 'raise) (zerop priority))
- (and (eq arg 'lower) (> priority maxnum))))
+ (and (eq arg 'lower) (>= priority maxnum))))
;; When moving item to another category, show the category before
;; prompting for its priority.
(unless (or arg (called-interactively-p 'any))
;; while setting priority.
(save-excursion (todo-category-select)))))
;; Prompt for priority only when the category has at least one
- ;; todo item.
- (when (> maxnum 1)
- (while (not priority)
- (setq candidate (read-number prompt
- (if (eq todo-default-priority 'first)
- 1 maxnum)))
- (setq prompt (when (or (< candidate 1) (> candidate maxnum))
- (format "Priority must be an integer between 1 and %d.\n"
- maxnum)))
- (unless prompt (setq priority candidate))))
+ ;; todo item or when passing the current priority as prefix arg.
+ (when (and (or (not priority) (= priority curnum))
+ (> maxnum 1))
+ (let* ((read-number-history (mapcar #'number-to-string
+ (if (eq todo-default-priority
+ 'first)
+ (number-sequence maxnum 1 -1)
+ (number-sequence 1 maxnum))))
+ (history-add-new-input nil)
+ (candidate (or priority
+ (read-number prompt
+ (if (eq todo-default-priority
+ 'first)
+ 1 maxnum))))
+ (success nil))
+ (while (not success)
+ (setq prompt
+ (cond
+ ((and (= candidate curnum)
+ ;; Allow same priority in a different category
+ ;; (only possible when called non-interactively).
+ (called-interactively-p 'any))
+ "New priority must be different from current priority: ")
+ (t (when (or (< candidate 1) (> candidate maxnum))
+ (format "Priority must be an integer between 1 and %d: "
+ maxnum)))))
+ (when prompt (setq candidate (read-number prompt)))
+ (unless prompt (setq priority candidate success t)))))
;; In Top Priorities buffer, an item's priority can be changed
;; wrt items in another category, but not wrt items in the same
;; category.
(insert (concat "\n" item1))
(should-error (todo-edit-quit) :type 'user-error))))
+(ert-deftest todo-test-item-insertion-with-priority-1 ()
+ "Test inserting new item when point is not on a todo item.
+When point is on the empty line at the end of the todo items
+section, insertion with priority setting should succeed."
+ (with-todo-test
+ (todo-test--show 1)
+ (goto-char (point-max))
+ ;; Now point should not be on a todo item.
+ (should-not (todo-item-start))
+ (let ((item "Point was on empty line at end of todo items section."))
+ (todo-test--insert-item item 1)
+ ;; Move point to item that was just inserted.
+ (goto-char (point-min))
+ (re-search-forward (concat todo-date-string-start todo-date-pattern
+ (regexp-quote todo-nondiary-end) " ")
+ (pos-eol) t)
+ (should (looking-at (regexp-quote item))))))
+
+(ert-deftest todo-test-item-insertion-with-priority-2 ()
+ "Test inserting new item when point is not on a todo item.
+When point is on the empty line at the end of the done items
+section, insertion with priority setting should succeed."
+ (with-todo-test
+ (todo-test--show 1)
+ (goto-char (point-max))
+ ;; See comment about recentering in todo-test-raise-lower-priority.
+ (set-window-buffer nil (current-buffer))
+ (todo-toggle-view-done-items)
+ (todo-next-item)
+ (goto-char (point-max))
+ ;; Now point should be at end of done items section, so not be on a
+ ;; todo item.
+ (should (todo-done-item-section-p))
+ (should-not (todo-item-start))
+ (let ((item "Point was on empty line at end of done items section."))
+ (todo-test--insert-item item 1)
+ ;; Move point to item that was just inserted.
+ (goto-char (point-min))
+ (re-search-forward (concat todo-date-string-start todo-date-pattern
+ (regexp-quote todo-nondiary-end) " ")
+ (pos-eol) t)
+ (should (looking-at (regexp-quote item))))))
+
+(ert-deftest todo-test-item-insertion-with-priority-3 ()
+ "Test inserting new item when point is not on a todo item.
+When point is on a done item, insertion with priority setting
+should succeed."
+ (with-todo-test
+ (todo-test--show 1)
+ (goto-char (point-max))
+ ;; See comment about recentering in todo-test-raise-lower-priority.
+ (set-window-buffer nil (current-buffer))
+ (todo-toggle-view-done-items)
+ (todo-next-item)
+ ;; Now point should be on first done item.
+ (should (and (todo-item-start) (todo-done-item-section-p)))
+ (let ((item "Point was on a done item."))
+ (todo-test--insert-item item 1)
+ ;; Move point to item that was just inserted.
+ (goto-char (point-min))
+ (re-search-forward (concat todo-date-string-start todo-date-pattern
+ (regexp-quote todo-nondiary-end) " ")
+ (pos-eol) t)
+ (should (looking-at (regexp-quote item))))))
+
(provide 'todo-mode-tests)
;;; todo-mode-tests.el ends here