Use the new syntax .+1d/3d for habit repeaters
This commit is contained in:
parent
9b36ab0ca9
commit
f93ace5368
23
doc/org.texi
23
doc/org.texi
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue