Use the new syntax .+1d/3d for habit repeaters

This commit is contained in:
John Wiegley 2009-10-20 14:11:43 -04:00
parent 9b36ab0ca9
commit f93ace5368
5 changed files with 49 additions and 57 deletions

View File

@ -3646,17 +3646,18 @@ called ``habits''. A habit has the following properties:
@enumerate
@item
The habit is a TODO, with a TODO keyword that represents an open state.
You have enabled the @code{habits} module by customizing the variable
@code{org-modules}.
@item
The habit is a TODO, with a TODO keyword representing an open state.
@item
The property @code{STYLE} is set to the value @code{habit}.
@item
The TODO has a scheduled date, with a @code{.+} style repeat interval.
@item
The TODO may also have a deadline, as long as it also has a @code{.+} style
repeat interval and it starts a number of days after the scheduled date equal
to the difference between the repeat interval lengths@footnote{Note that if
you don't set this right, Org will alert you as to what's incorrect about the
habit definition.}.
The TODO may also have minimum and maximum ranges specified by using the
syntax @samp{.+2d/3d}, which says that you want to do the task at least every
three days, but at most every two days.
@item
You must also have state logging for the @code{DONE} state enabled, in order
for historical data to be represented in the consistency graph. If it's not
@ -3669,7 +3670,7 @@ actual habit with some history:
@example
** TODO Shave
SCHEDULED: <2009-10-17 Sat .+2d> DEADLINE: <2009-10-19 Mon .+4d>
SCHEDULED: <2009-10-17 Sat .+2d/4d>
- State "DONE" from "TODO" [2009-10-15 Thu]
- State "DONE" from "TODO" [2009-10-12 Mon]
- State "DONE" from "TODO" [2009-10-10 Sat]
@ -3687,10 +3688,10 @@ actual habit with some history:
@end example
What this habit says is: I want to shave at most every 2 days (given by the
@code{SCHEDULED} date and repeat interval) at least every 4 days (given by
the @code{DEADLINE} date and repeat interval). If today is the 15th, then
the habit first appears in the agenda on Oct 17, after the minimum of 2 days
has elapsed, and will appear overdue on Oct 19, after four days have elapsed.
@code{SCHEDULED} date and repeat interval) and at least every 4 days. If
today is the 15th, then the habit first appears in the agenda on Oct 17,
after the minimum of 2 days has elapsed, and will appear overdue on Oct 19,
after four days have elapsed.
What's really useful about habits is that they are displayed along with a
conistency graph, to show how consistent you've been at getting that task

View File

@ -1,5 +1,16 @@
2009-10-20 John Wiegley <jwiegley@gmail.com>
* org-habit.el (org-habit-duration-to-days): Made this function
more general.
(org-habit-parse-todo): Parse the new ".+N/N" style repeater.
* org-agenda.el (org-agenda-get-deadlines): Removed all mention of
habits, since they don't use DEADLINE anymore.
* org.el (org-repeat-re, org-display-custom-time)
(org-timestamp-change): Extended to support the new ".+N/N"
syntax, used for habits.
* org-clock.el (org-clock-resolve-clock): Fixed an incorrect
variable reference.

View File

@ -4231,7 +4231,7 @@ the documentation of `org-diary'."
(regexp org-deadline-time-regexp)
(todayp (org-agenda-todayp date)) ; DATE bound by calendar
(d1 (calendar-absolute-from-gregorian date)) ; DATE bound by calendar
d2 diff dfrac wdays pos pos1 category tags habitp
d2 diff dfrac wdays pos pos1 category tags
ee txt head face s todo-state upcomingp donep timestr)
(goto-char (point-min))
(while (re-search-forward regexp nil t)
@ -4244,12 +4244,6 @@ the documentation of `org-diary'."
(match-string 1) d1 'past
org-agenda-repeating-timestamp-show-all)
diff (- d2 d1)
;; Never show habits as deadline entries, only as scheduled
;; entries. The habit code already requires that every habit
;; have a scheduled date if it has a deadline, and that the
;; scheduled date is prior to the deadline.
habitp (and (functionp 'org-is-habit-p)
(org-is-habit-p))
wdays (org-get-wdays s)
dfrac (/ (* 1.0 (- wdays diff)) (max wdays 1))
upcomingp (and todayp (> diff 0)))
@ -4258,8 +4252,7 @@ the documentation of `org-diary'."
;; Past-due deadlines are only shown on the current date
(if (and (or (and (<= diff wdays)
(and todayp (not org-agenda-only-exact-dates)))
(= diff 0))
(not habitp))
(= diff 0)))
(save-excursion
(setq todo-state (org-get-todo-state))
(setq donep (member todo-state org-done-keywords))
@ -4402,7 +4395,8 @@ FRACTION is what fraction of the head-warning time has passed."
(when txt
(setq face
(cond
(pastschedp 'org-scheduled-previously)
((and (not habitp) pastschedp)
'org-scheduled-previously)
(todayp 'org-scheduled-today)
(t 'org-scheduled)))
(org-add-props txt props

View File

@ -123,7 +123,7 @@ relative to the current effective time."
:type 'color)
(defun org-habit-duration-to-days (ts)
(if (string-match "\\([0-9]+\\)\\([dwmy]\\)\\'" ts)
(if (string-match "\\([0-9]+\\)\\([dwmy]\\)" ts)
;; lead time is specified.
(floor (* (string-to-number (match-string 1 ts))
(cdr (assoc (match-string 2 ts)
@ -148,41 +148,27 @@ This list represents a \"habit\" for the rest of this module."
(save-excursion
(if pom (goto-char pom))
(assert (org-is-habit-p (point)))
(let ((scheduled (org-get-scheduled-time (point)))
(scheduled-repeat (org-get-repeat "SCHEDULED"))
(deadline (org-get-deadline-time (point)))
(deadline-repeat (org-get-repeat "DEADLINE")))
(let* ((scheduled (org-get-scheduled-time (point)))
(scheduled-repeat (org-get-repeat "SCHEDULED"))
(sr-days (org-habit-duration-to-days scheduled-repeat))
(end (org-entry-end-position))
closed-dates deadline dr-days)
(unless scheduled
(error "Habit has no scheduled date"))
(unless scheduled-repeat
(error "Habit has no scheduled repeat period"))
(unless (string-match "\\`\\.\\+[0-9]+" scheduled-repeat)
(error "Habit's scheduled repeat period does not match `.+[0-9]*'"))
(if (and deadline (not deadline-repeat))
(error "Habit has a deadline, but no deadline repeat period"))
(if (and deadline
(not (string-match "\\`\\.\\+[0-9]+" scheduled-repeat)))
(error "Habit's deadline repeat period does not match `.+[0-9]*'"))
(let ((sr-days (org-habit-duration-to-days scheduled-repeat))
(dr-days (and deadline-repeat
(org-habit-duration-to-days deadline-repeat))))
(when (and scheduled deadline)
(cond
((time-less-p deadline scheduled)
(error "Habit's deadline date is before the scheduled date"))
((< dr-days sr-days)
(error "Habit's deadline repeat period is less than scheduled"))
((/= (- (time-to-days deadline)
(time-to-days scheduled))
(- dr-days sr-days))
(error "Habit's deadline and scheduled period lengths are off"))))
(let ((end (org-entry-end-position))
closed-dates)
(org-back-to-heading t)
(while (re-search-forward "- State \"DONE\".*\\[\\([^]]+\\)\\]" end t)
(push (org-time-string-to-time (match-string-no-properties 1))
closed-dates))
(list scheduled sr-days deadline dr-days closed-dates))))))
(when (string-match "/\\([0-9]+[dwmy]\\)" scheduled-repeat)
(setq dr-days (org-habit-duration-to-days
(match-string-no-properties 1 scheduled-repeat)))
(if (<= dr-days sr-days)
(error "Habit's deadline repeat period is less than or equal to scheduled"))
(setq deadline (time-add scheduled
(days-to-time (- dr-days sr-days)))))
(org-back-to-heading t)
(while (re-search-forward "- State \"DONE\".*\\[\\([^]]+\\)\\]" end t)
(push (org-time-string-to-time (match-string-no-properties 1))
closed-dates))
(list scheduled sr-days deadline dr-days closed-dates))))
(defsubst org-habit-scheduled (habit)
(nth 0 habit))
@ -215,7 +201,7 @@ Habits are assigned colors on the following basis:
(s-repeat (org-habit-scheduled-repeat habit))
(scheduled-end (time-add scheduled (days-to-time s-repeat)))
(d-repeat (org-habit-deadline-repeat habit))
(deadline (if scheduled-time
(deadline (if (and scheduled-time d-repeat)
(time-add scheduled-time
(days-to-time (- d-repeat s-repeat)))
(org-habit-deadline habit))))

View File

@ -475,7 +475,7 @@ An entry can be toggled between QUOTE and normal with
:type 'string)
(defconst org-repeat-re
"<[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^>\n]*\\([.+]+?\\+[0-9]+[dwmy]\\)"
"<[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^>\n]*?\\([.+]?\\+[0-9]+[dwmy]\\(/[0-9]+[dwmy]\\)?\\)"
"Regular expression for specifying repeated events.
After a match, group 1 contains the repeat expression.")
@ -13052,7 +13052,7 @@ The command returns the inserted time stamp."
t1 w1 with-hm tf time str w2 (off 0))
(save-match-data
(setq t1 (org-parse-time-string ts t))
(if (string-match "\\(-[0-9]+:[0-9]+\\)?\\( [.+]?\\+[0-9]+[dwmy]\\)?\\'" ts)
(if (string-match "\\(-[0-9]+:[0-9]+\\)?\\( [.+]?\\+[0-9]+[dwmy]\\(/[0-9]+[dwmy]\\)?\\)?\\'" ts)
(setq off (- (match-end 0) (match-beginning 0)))))
(setq end (- end off))
(setq w1 (- end beg)
@ -13565,7 +13565,7 @@ in the timestamp determines what will be changed."
ts (match-string 0))
(replace-match "")
(if (string-match
"\\(\\(-[012][0-9]:[0-5][0-9]\\)?\\( +[.+]?[-+][0-9]+[dwmy]\\)*\\)[]>]"
"\\(\\(-[012][0-9]:[0-5][0-9]\\)?\\( +[.+]?[-+][0-9]+[dwmy]\\(/[0-9]+[dwmy]\\)?\\)*\\)[]>]"
ts)
(setq extra (match-string 1 ts)))
(if (string-match "^.\\{10\\}.*?[0-9]+:[0-9][0-9]" ts)