diff --git a/ChangeLog b/ChangeLog index b75957920..0ca905fb2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,13 @@ html buffer. Side effects first need to be studied carefully. (org-get-tags-at): Fix the structure of the condition-case statement. +2008-03-07 Carsten Dominik + + * org.el (org-ts-regexp0, org-repeat-re, org-display-custom-time) + (org-timestamp-change): Fix regulear expressions to swallow the + extra character for repeat-shift control. + (org-auto-repeat-maybe): Implement the new repeater mechanisms. + 2008-03-06 Bastien Guerry * org.el (org-get-legal-level): Aliased to `org-get-valid-level'. diff --git a/ORGWEBPAGE/Changes.org b/ORGWEBPAGE/Changes.org index 459f3a594..1e4485c90 100644 --- a/ORGWEBPAGE/Changes.org +++ b/ORGWEBPAGE/Changes.org @@ -136,9 +136,23 @@ Based on proposals by Bastien. + + - New repeaters that shift a date relative to today, or that + make sure that the next date is in the future. For example: + + :** TODO Call Father + : DEADLINE: <2008-02-10 Sun ++1w> + : Marking this DONE will shift the date by at least one week, + : but also by as many weeks as it takes to get this date into + : the future. However, it stays on a Sunday, even if you called + : and marked it done on Saturday. + :** TODO Check the batteries in the smoke detectors + : DEADLINE: <2005-11-01 Tue .+1m> + : Marking this DONE will shift the date to one month after + : today. + + Proposed by Wanrong Lin and Rainer Stengle. - - * Version 5.22 ** Incompatible changes diff --git a/org.el b/org.el index c29fca1f8..d01586c38 100644 --- a/org.el +++ b/org.el @@ -355,9 +355,7 @@ An entry can be toggled between QUOTE and normal with :type 'string) (defconst org-repeat-re -; (concat "\\(?:\\<\\(?:" org-scheduled-string "\\|" org-deadline-string "\\)" -; " +<[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]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^>\n]*\\([.+]?\\+[0-9]+[dwmy]\\)" "Regular expression for specifying repeated events. After a match, group 1 contains the repeat expression.") @@ -5287,9 +5285,10 @@ This should be called after the variable `org-link-types' has changed." "Regular expression for fast time stamp matching.") (defconst org-ts-regexp-both "[[<]\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [^\r\n>]*?\\)[]>]" "Regular expression for fast time stamp matching.") -(defconst org-ts-regexp0 "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)\\([^]0-9>\r\n]*\\)\\(\\([0-9]\\{2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" +(defconst org-ts-regexp0 "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\) *\\([^]-+0-9>\r\n ]*\\)\\( \\([0-9]\\{2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" "Regular expression matching time strings for analysis. -This one does not require the space after the date.") +This one does not require the space after the date, so it can be used +on a string that terminates immediately after the date.") (defconst org-ts-regexp1 "\\(\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\) +\\([^]-+0-9>\r\n ]*\\)\\( \\([0-9]\\{2\\}\\):\\([0-9]\\{2\\}\\)\\)?\\)" "Regular expression matching time strings for analysis.") (defconst org-ts-regexp2 (concat "<" org-ts-regexp1 "[^>\n]\\{0,16\\}>") @@ -15022,7 +15021,8 @@ This function is run automatically after each state change to a DONE state." (msg "Entry repeats: ") (org-log-done nil) (org-todo-log-states nil) - re type n what ts) + (nshiftmax 10) (nshift 0) + re type n what ts mb0 time) (when repeat (if (eq org-log-repeat t) (setq org-log-repeat 'state)) (org-todo (if (eq interpret 'type) last-state head)) @@ -15042,11 +15042,36 @@ This function is run automatically after each state change to a DONE state." re (save-excursion (outline-next-heading) (point)) t) (setq type (if (match-end 1) org-scheduled-string (if (match-end 3) org-deadline-string "Plain:")) - ts (match-string (if (match-end 2) 2 (if (match-end 4) 4 0)))) - (when (string-match "\\([-+]?[0-9]+\\)\\([dwmy]\\)" ts) - (setq n (string-to-number (match-string 1 ts)) - what (match-string 2 ts)) + ts (match-string (if (match-end 2) 2 (if (match-end 4) 4 0))) + mb0 (match-beginning 0)) + (when (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([dwmy]\\)" ts) + (setq n (string-to-number (match-string 2 ts)) + what (match-string 3 ts)) (if (equal what "w") (setq n (* n 7) what "d")) + ;; Preparation, see if we need to modify the start date for the change + (when (match-end 1) + (setq time (save-match-data (org-time-string-to-time ts))) + (cond + ((equal (match-string 1 ts) ".") + ;; Shift starting date to today + (org-timestamp-change + (- (time-to-days (current-time)) (time-to-days time)) + 'day)) + ((equal (match-string 1 ts) "+") + (while (< (time-to-days time) (time-to-days (current-time))) + (when (= (incf nshift) nshiftmax) + (or (y-or-n-p (message "%d repeater intervals were not enough to shift date past today. Continue? " nshift)) + (error "Abort"))) + (org-timestamp-change n (cdr (assoc what whata))) + (sit-for .0001) ;; so we can watch the date shifting + (org-at-timestamp-p t) + (setq ts (match-string 1)) + (setq time (save-match-data (org-time-string-to-time ts)))) + (org-timestamp-change (- n) (cdr (assoc what whata))) + ;; rematch, so that we have everything in place for the real shift + (org-at-timestamp-p t) + (setq ts (match-string 1)) + (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([dwmy]\\)" ts)))) (org-timestamp-change n (cdr (assoc what whata))) (setq msg (concat msg type org-last-changed-timestamp " ")))) (setq org-log-post-message msg) @@ -18219,7 +18244,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]\\)?\\'" ts) (setq off (- (match-end 0) (match-beginning 0))))) (setq end (- end off)) (setq w1 (- end beg) @@ -18699,7 +18724,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]\\)*\\)[]>]" ts) (setq extra (match-string 1 ts))) (if (string-match "^.\\{10\\}.*?[0-9]+:[0-9][0-9]" ts) diff --git a/org.texi b/org.texi index d66637693..777389e0f 100644 --- a/org.texi +++ b/org.texi @@ -4397,6 +4397,29 @@ As a consequence of shifting the base date, this entry will no longer be visible in the agenda when checking past dates, but all future instances will be visible. +With the @samp{+1m} cookie, the date shift will always be exactly one +month. So if you have not payed the rent for three months, marking this +entry DONE will still keep it as an overdue deadline. Depending on the +task, this may not be the best way to handle it. For example, if you +forgot to call you father for 3 weeks, it does not make sense to call +her 3 times in a single day to make up for it. Finally, there are tasks +like changing batteries which should always repeat a certain time +@i{after} the last time you did it. For these tasks, Org-mode has +special repeaters markes with @samp{++} and samp{.+}. For example: + +@example +** TODO Call Father + DEADLINE: <2008-02-10 Sun ++1w> + Marking this DONE will shift the date by at least one week, + but also by as many weeks as it takes to get this date into + the future. However, it stays on a Sunday, even if you called + and marked it done on Saturday. +** TODO Check the batteries in the smoke detectors + DEADLINE: <2005-11-01 Tue .+1m> + Marking this DONE will shift the date to one month after + today. +@end example + You may have both scheduling and deadline information for a specific task - just make sure that the repeater intervals on both are the same.