Improve repeated entries handling

* lisp/org-agenda.el (org-agenda-show-future-repeats):
(org-agenda-prefer-last-repeat): New variables.
(org-agenda-repeating-timestamp-show-all): Remove variable.
(org-agenda-get-timestamps):
(org-agenda-get-deadlines):
(org-agenda-get-scheduled): Use new variables.
This commit is contained in:
Nicolas Goaziou 2016-12-03 09:04:42 +01:00
parent 1658cb26ff
commit 3a4d16deab
2 changed files with 139 additions and 114 deletions

View File

@ -24,6 +24,9 @@ into
: (file (lambda () (sexp)))
** New features
*** Agenda
**** New variable : ~org-agenda-show-future-repeats~
**** New variable : ~org-agenda-prefer-last-repeat~
*** Babel
**** Clojure: new setting ~org-babel-clojure-sync-nrepl-timeout~
@ -66,6 +69,13 @@ value of the code will be displayed in the results section.
*** Horizontal rules are no longer ignored in LaTeX table math mode
** Removed options
*** ~org-agenda-repeating-timestamp-show-all~ is removed.
For an equivalent to a ~nil~ value, set
~org-agenda-show-future-repeats~ to nil and
~org-agenda-prefer-last-repeat~ to ~t~.
* Version 9.0
** Incompatible changes

View File

@ -1260,17 +1260,38 @@ Custom commands can set this variable in the options section."
:version "24.1"
:type 'boolean)
(defcustom org-agenda-repeating-timestamp-show-all t
"Non-nil means show all occurrences of a repeating stamp in the agenda.
When set to a list of strings, only show occurrences of repeating
stamps for these TODO keywords. When nil, only one occurrence is
shown, either today or the nearest into the future."
(defcustom org-agenda-show-future-repeats t
"Non-nil shows repeated entries in the future part of the agenda.
When set to the symbol `next' only the first future repeat is shown."
:group 'org-agenda-daily/weekly
:type '(choice
(const :tag "Show repeating stamps" t)
(repeat :tag "Show repeating stamps for these TODO keywords"
(string :tag "TODO Keyword"))
(const :tag "Don't show repeating stamps" nil)))
(const :tag "Show all repeated entries" t)
(const :tag "Show next repeated entry" next)
(const :tag "Do not show repeated entries" nil))
:version "25.2"
:package-version '(Org . "9.1")
:safe #'symbolp)
(defcustom org-agenda-prefer-last-repeat nil
"Non-nil sets date for repeated entries to their last repeat.
When non-nil, a repeated entry is shows at its latest repeat
date, possibly being today, instead of the its base date, even if
it wasn't marked as done. This setting is useful if you do not
always mark repeated entries as DONE and, yet, consider that
reaching repeat date starts the task anew.
When set to a list of strings, prefer last repeats only for
entries with these TODO keywords."
:group 'org-agenda-daily/weekly
:type '(choice
(const :tag "Prefer last repeat" t)
(const :tag "Prefer base date" nil)
(repeat :tag "Prefer last repeat for entries with these TODO keywords"
(string :tag "TODO keyword")))
:version "25.2"
:package-version '(Org . "9.1")
:safe (lambda (x) (or (booleanp x) (consp x))))
(defcustom org-scheduled-past-days 10000
"Number of days to continue listing scheduled items not marked DONE.
@ -5640,9 +5661,6 @@ displayed in agenda view."
(looking-at org-ts-regexp-both)
(match-string 0))))
(todo-state (org-get-todo-state))
(show-all (or (eq org-agenda-repeating-timestamp-show-all t)
(member todo-state
org-agenda-repeating-timestamp-show-all)))
(warntime (get-text-property (point) 'org-appt-warntime))
(done? (member todo-state org-done-keywords)))
;; Possibly skip done tasks.
@ -5651,22 +5669,27 @@ displayed in agenda view."
;; S-exp entry doesn't match current day: skip it.
(when (and sexp-entry (not (org-diary-sexp-entry sexp-entry "" date)))
(throw :skip nil))
;; When time-stamp doesn't match CURRENT but has a repeater,
;; make sure it repeats on CURRENT. Furthermore, if
;; SHOW-ALL is nil, ensure that repeats are only the first
;; before and the first after today.
(when (and repeat
(if show-all
(/= current
(org-agenda--timestamp-to-absolute
repeat current 'future (current-buffer) pos))
(and (/= current
(org-agenda--timestamp-to-absolute
repeat today 'past (current-buffer) pos))
(/= current
(org-agenda--timestamp-to-absolute
repeat today 'future (current-buffer) pos)))))
(throw :skip nil))
;; A repeating time stamp is shown at its base date, and at
;; every repeated date in the future.
(when repeat
(let* ((past
(if (or (eq org-agenda-prefer-last-repeat t)
(member todo-state org-agenda-prefer-last-repeat))
(org-agenda--timestamp-to-absolute
repeat today 'past (current-buffer) pos)
(org-agenda--timestamp-to-absolute repeat)))
(future
(cond
((<= current today) past)
((not org-agenda-show-future-repeats) past)
(t
(let ((base (if (eq org-agenda-show-future-repeats 'next)
(1+ today)
current)))
(org-agenda--timestamp-to-absolute
repeat base 'future (current-buffer) pos))))))
(when (and (/= current past) (/= current future))
(throw :skip nil))))
(save-excursion
(re-search-backward org-outline-regexp-bol nil t)
;; Possibly skip time-stamp when a deadline is set.
@ -6067,22 +6090,29 @@ specification like [h]h:mm."
(pos (1- (match-beginning 1)))
(todo-state (save-match-data (org-get-todo-state)))
(done? (member todo-state org-done-keywords))
(show-all (or (eq org-agenda-repeating-timestamp-show-all t)
(member todo-state
org-agenda-repeating-timestamp-show-all)))
;; DEADLINE is the bare deadline date, i.e., without
;; any repeater, or the last repeat if SHOW-ALL is
;; non-nil. REPEAT is closest repeat after CURRENT, if
;; all repeated time stamps are to be shown, or after
;; TODAY otherwise. REPEAT only applies to future
;; dates.
(deadline (if show-all (org-agenda--timestamp-to-absolute s)
(org-agenda--timestamp-to-absolute
s today 'past (current-buffer) pos)))
;; DEADLINE is the deadline date for the entry. It is
;; either the base date or the last repeat, according
;; to `org-agenda-prefer-last-repeat'.
(deadline
(if (or (eq org-agenda-prefer-last-repeat t)
(member todo-state org-agenda-prefer-last-repeat))
(org-agenda--timestamp-to-absolute
s today 'past (current-buffer) pos)
(org-agenda--timestamp-to-absolute s)))
;; REPEAT is the future repeat closest from CURRENT,
;; according to `org-agenda-show-future-repeats'. If
;; the latter is nil, or if the time stamp has no
;; repeat part, default to DEADLINE.
(repeat
(if (< current today) deadline
(org-agenda--timestamp-to-absolute
s (if show-all current today) 'future (current-buffer) pos)))
(cond
((<= current today) deadline)
((not org-agenda-show-future-repeats) deadline)
(t
(let ((base (if (eq org-agenda-show-future-repeats 'next)
(1+ today)
current)))
(org-agenda--timestamp-to-absolute
s base 'future (current-buffer) pos)))))
(diff (- deadline current))
(suppress-prewarning
(let ((scheduled
@ -6105,16 +6135,16 @@ specification like [h]h:mm."
(let ((org-deadline-warning-days suppress-prewarning))
(org-get-wdays s))
(org-get-wdays s))))
;; When to show a deadline in the calendar: if the
;; expiration is within WDAYS warning time. Past-due
;; deadlines are only shown on today agenda.
(when (cond ((= current deadline) nil)
((< deadline today)
(and (not today?)
(or (< current today) (/= repeat current))))
((> deadline current)
(or (not today?) (> diff wdays)))
(t (/= repeat current)))
;; Display deadlines items at base date (DEADLINE), today,
;; if deadline is overdue or if the expiration of the
;; upcoming deadline is within WDAYS warning time. Also,
;; show any repeat past today.
(when (or (and (/= current deadline)
(/= current today)
(/= current repeat))
(and today?
(> deadline current)
(> diff wdays)))
(throw :skip nil))
;; Possibly skip done tasks.
(when (and done?
@ -6125,8 +6155,8 @@ specification like [h]h:mm."
(re-search-backward "^\\*+[ \t]+" nil t)
(goto-char (match-end 0))
(let* ((category (org-get-category))
(level
(make-string (org-reduced-level (org-outline-level)) ?\s))
(level (make-string (org-reduced-level (org-outline-level))
?\s))
(head (buffer-substring (point) (line-end-position)))
(inherited-tags
(or (eq org-agenda-show-inherited-tags 'always)
@ -6148,23 +6178,16 @@ specification like [h]h:mm."
(item
(org-agenda-format-item
;; Insert appropriate suffixes before deadlines.
;; Those only apply to today agenda.
(pcase-let ((`(,now ,future ,past)
org-agenda-deadline-leaders))
(cond
;; Future (i.e., repeated) deadlines are
;; displayed as new headlines.
((> current today) now)
;; When SHOW-ALL is nil, prefer repeated
;; deadlines over reminders of past deadlines.
((and (not show-all) (= repeat today)) now)
((= deadline current) now)
((< deadline current) (format past (- diff)))
(t (format future diff))))
head level category tags
(and (or (= repeat current) (= deadline current))
time)))
((and today? (< deadline today)) (format past (- diff)))
((and today? (> deadline today)) (format future diff))
(t now)))
head level category tags time))
(face (org-agenda-deadline-face
(- 1 (/ (float (- deadline current)) (max wdays 1)))))
(- 1 (/ (float diff) (max wdays 1)))))
(upcoming? (and today? (> deadline today)))
(warntime (get-text-property (point) 'org-appt-warntime)))
(org-add-props item props
@ -6178,9 +6201,7 @@ specification like [h]h:mm."
;; Overdue deadlines get the highest priority
;; increase, then imminent deadlines and eventually
;; more distant deadlines.
(let ((adjust (cond ((not today?) 0)
((and (not show-all) (= repeat current)) 0)
(t (- diff)))))
(let ((adjust (if today? (- diff) 0)))
(+ adjust (org-get-priority item)))
'todo-state todo-state
'type (if upcoming? "upcoming-deadline" "deadline")
@ -6230,25 +6251,29 @@ scheduled items with an hour specification like [h]h:mm."
(pos (1- (match-beginning 1)))
(todo-state (save-match-data (org-get-todo-state)))
(donep (member todo-state org-done-keywords))
(show-all (or (eq org-agenda-repeating-timestamp-show-all t)
(member todo-state
org-agenda-repeating-timestamp-show-all)))
;; SCHEDULE is the bare scheduled date, i.e., without
;; any repeater if non-nil, or last repeat if SHOW-ALL
;; is nil. REPEAT is the closest repeat after CURRENT,
;; if all repeated time stamps are to be shown, or
;; after TODAY otherwise. REPEAT only applies to
;; future dates.
(schedule (if show-all (org-agenda--timestamp-to-absolute s)
(org-agenda--timestamp-to-absolute
s today 'past (current-buffer) pos)))
(repeat (cond ((< current today) schedule)
(show-all
(org-agenda--timestamp-to-absolute
s current 'future (current-buffer) pos))
(t
(org-agenda--timestamp-to-absolute
s today 'future (current-buffer) pos))))
;; SCHEDULE is the scheduled date for the entry. It is
;; either the bare date or the last repeat, according
;; to `org-agenda-prefer-last-repeat'.
(schedule
(if (or (eq org-agenda-prefer-last-repeat t)
(member todo-state org-agenda-prefer-last-repeat))
(org-agenda--timestamp-to-absolute
s today 'past (current-buffer) pos)
(org-agenda--timestamp-to-absolute s)))
;; REPEAT is the future repeat closest from CURRENT,
;; according to `org-agenda-show-future-repeats'. If
;; the latter is nil, or if the time stamp has no
;; repeat part, default to SCHEDULE.
(repeat
(cond
((<= current today) schedule)
((not org-agenda-show-future-repeats) schedule)
(t
(let ((base (if (eq org-agenda-show-future-repeats 'next)
(1+ today)
current)))
(org-agenda--timestamp-to-absolute
s base 'future (current-buffer) pos)))))
(diff (- current schedule))
(warntime (get-text-property (point) 'org-appt-warntime))
(pastschedp (< schedule today))
@ -6265,9 +6290,7 @@ scheduled items with an hour specification like [h]h:mm."
(- org-agenda-skip-scheduled-delay-if-deadline))
((eq org-agenda-skip-scheduled-delay-if-deadline
'post-deadline)
;; Set delay to no later than DEADLINE. If
;; DEADLINE has a repeater, compare last schedule
;; repeat and last deadline repeat.
;; Set delay to no later than DEADLINE.
(min (- schedule deadline) org-scheduled-delay-days))
(t 0))))
(ddays
@ -6291,9 +6314,9 @@ scheduled items with an hour specification like [h]h:mm."
(when (or (and (> ddays 0) (< diff ddays))
(> diff org-scheduled-past-days)
(> schedule current)
(and (< schedule current)
(not todayp)
(/= repeat current)))
(and (/= current schedule)
(/= current today)
(/= current repeat)))
(throw :skip nil)))
;; Possibly skip done tasks.
(when (and donep
@ -6309,7 +6332,9 @@ scheduled items with an hour specification like [h]h:mm."
habitp))
nil)
(`repeated-after-deadline
(>= repeat (time-to-days (org-get-deadline-time (point)))))
(let ((deadline (time-to-days
(org-get-deadline-time (point)))))
(and (<= schedule deadline) (> current deadline))))
(`not-today pastschedp)
(`t t)
(_ nil))
@ -6336,8 +6361,8 @@ scheduled items with an hour specification like [h]h:mm."
(memq 'agenda
org-agenda-use-tag-inheritance)))))
(tags (org-get-tags-at nil (not inherited-tags)))
(level
(make-string (org-reduced-level (org-outline-level)) ?\s))
(level (make-string (org-reduced-level (org-outline-level))
?\s))
(head (buffer-substring (point) (line-end-position)))
(time
(cond
@ -6349,21 +6374,11 @@ scheduled items with an hour specification like [h]h:mm."
(t 'time)))
(item
(org-agenda-format-item
(pcase-let ((`(,first ,next) org-agenda-scheduled-leaders))
(cond
;; If CURRENT is in the future, don't use past
;; scheduled prefix.
((> current today) first)
;; SHOW-ALL focuses on future repeats. If one
;; such repeat happens today, ignore late
;; schedule reminder. However, still report
;; such reminders when repeat happens later.
((and (not show-all) (= repeat today)) first)
;; Initial report.
((= schedule current) first)
;; Subsequent reminders. Count from base
;; schedule.
(t (format next (1+ diff)))))
(pcase-let ((`(,first ,past) org-agenda-scheduled-leaders))
;; Show a reminder of a past scheduled today.
(if (and todayp pastschedp)
(format past (1+ diff))
first))
head level category tags time nil habitp))
(face (cond ((and (not habitp) pastschedp)
'org-scheduled-previously)