Browse Source

Implemented new repeater mechanisms.

There mechanisms were asked for by Wanrong Lin and Reiner Stengele and
have the goal to make sure the new deadline/scheduled/time is in
the future, and may be chosen relative to the current date.
Carsten Dominik 17 years ago
parent
commit
3047e9faee
4 changed files with 94 additions and 23 deletions
  1. 7 0
      ChangeLog
  2. 16 2
      ORGWEBPAGE/Changes.org
  3. 48 21
      org.el
  4. 23 0
      org.texi

+ 7 - 0
ChangeLog

@@ -1,3 +1,10 @@
+2008-03-07  Carsten Dominik  <dominik@science.uva.nl>
+
+	* 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  <bzg@altern.org>
 2008-03-06  Bastien Guerry  <bzg@altern.org>
 
 
 	* org.el (org-get-legal-level): Aliased to `org-get-valid-level'.
 	* org.el (org-get-legal-level): Aliased to `org-get-valid-level'.

+ 16 - 2
ORGWEBPAGE/Changes.org

@@ -132,9 +132,23 @@
 
 
      Based on proposals by Bastien.
      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
 * Version 5.22
 
 
 ** Incompatible changes
 ** Incompatible changes

+ 48 - 21
org.el

@@ -355,9 +355,7 @@ An entry can be toggled between QUOTE and normal with
   :type 'string)
   :type 'string)
 
 
 (defconst org-repeat-re
 (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.
   "Regular expression for specifying repeated events.
 After a match, group 1 contains the repeat expression.")
 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.")
   "Regular expression for fast time stamp matching.")
 (defconst org-ts-regexp-both "[[<]\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [^\r\n>]*?\\)[]>]"
 (defconst org-ts-regexp-both "[[<]\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [^\r\n>]*?\\)[]>]"
   "Regular expression for fast time stamp matching.")
   "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.
   "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\\}\\)\\)?\\)"
 (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.")
   "Regular expression matching time strings for analysis.")
 (defconst org-ts-regexp2 (concat "<" org-ts-regexp1 "[^>\n]\\{0,16\\}>")
 (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: ")
 	 (msg "Entry repeats: ")
 	 (org-log-done nil)
 	 (org-log-done nil)
 	 (org-todo-log-states nil)
 	 (org-todo-log-states nil)
-	 re type n what ts)
+	 (nshiftmax 10) (nshift 0)
+	 re type n what ts mb0 time)
     (when repeat
     (when repeat
       (if (eq org-log-repeat t) (setq org-log-repeat 'state))
       (if (eq org-log-repeat t) (setq org-log-repeat 'state))
       (org-todo (if (eq interpret 'type) last-state head))
       (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)
 	      re (save-excursion (outline-next-heading) (point)) t)
 	(setq type (if (match-end 1) org-scheduled-string
 	(setq type (if (match-end 1) org-scheduled-string
 		     (if (match-end 3) org-deadline-string "Plain:"))
 		     (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"))
 	  (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)))
 	  (org-timestamp-change n (cdr (assoc what whata)))
 	  (setq msg (concat msg type org-last-changed-timestamp " "))))
 	  (setq msg (concat msg type org-last-changed-timestamp " "))))
       (setq org-log-post-message msg)
       (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))
 	 t1 w1 with-hm tf time str w2 (off 0))
     (save-match-data
     (save-match-data
       (setq t1 (org-parse-time-string ts t))
       (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 off (- (match-end 0) (match-beginning 0)))))
     (setq end (- end off))
     (setq end (- end off))
     (setq w1 (- end beg)
     (setq w1 (- end beg)
@@ -18699,7 +18724,7 @@ in the timestamp determines what will be changed."
 	    ts (match-string 0))
 	    ts (match-string 0))
       (replace-match "")
       (replace-match "")
       (if (string-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)
 	   ts)
 	  (setq extra (match-string 1 ts)))
 	  (setq extra (match-string 1 ts)))
       (if (string-match "^.\\{10\\}.*?[0-9]+:[0-9][0-9]" ts)
       (if (string-match "^.\\{10\\}.*?[0-9]+:[0-9][0-9]" ts)
@@ -23329,15 +23354,16 @@ the tags of the current headline come last."
 	(goto-char (or pos (point)))
 	(goto-char (or pos (point)))
 	(save-match-data
 	(save-match-data
 	  (condition-case nil
 	  (condition-case nil
-	      (org-back-to-heading t)
-	      (while (not (equal lastpos (point)))
-		(setq lastpos (point))
-		(if (looking-at (org-re "[^\r\n]+?:\\([[:alnum:]_@:]+\\):[ \t]*$"))
-		    (setq tags (append (org-split-string
-					(org-match-string-no-properties 1) ":")
-				       tags)))
-	      (or org-use-tag-inheritance (error ""))
-	      (org-up-heading-all 1))
+	      (progn
+		(org-back-to-heading t)
+		(while (not (equal lastpos (point)))
+		  (setq lastpos (point))
+		  (if (looking-at (org-re "[^\r\n]+?:\\([[:alnum:]_@:]+\\):[ \t]*$"))
+		      (setq tags (append (org-split-string
+					  (org-match-string-no-properties 1) ":")
+					 tags)))
+		  (or org-use-tag-inheritance (error ""))
+		  (org-up-heading-all 1)))
 	    (error nil))))
 	    (error nil))))
       tags)))
       tags)))
 
 
@@ -26102,6 +26128,7 @@ lang=\"%s\" xml:lang=\"%s\">
 			  (make-string n ?x)))))
 			  (make-string n ?x)))))
 
 
       (or to-buffer (progn (save-buffer) (kill-buffer (current-buffer))))
       (or to-buffer (progn (save-buffer) (kill-buffer (current-buffer))))
+
       (goto-char (point-min))
       (goto-char (point-min))
       (message "Exporting... done")
       (message "Exporting... done")
       (if (eq to-buffer 'string)
       (if (eq to-buffer 'string)

+ 23 - 0
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
 visible in the agenda when checking past dates, but all future instances
 will be visible.
 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
 You may have both scheduling and deadline information for a specific
 task - just make sure that the repeater intervals on both are the same.
 task - just make sure that the repeater intervals on both are the same.