Browse Source

org-element: Improve timestamps parsing

* lisp/org-element.el (org-element-timestamp-parser): Modify timestamp
  objects properties.
(org-element-headline-parser, org-element-inlinetask-parser): Remove
`:timestamp' and `:clock' property.  Add `:clockedp' property.  Also,
set `:closed', `:deadline' and `:scheduled' values to timestamp
objects, not strings.  Small refactoring.
(org-element-clock-parser): Rename `:time' property into `:duration'.
Also, set `:value' value as a timestamp object, not a string.
(org-element-planning-parser): Set `:closed', `:deadline' and
`:scheduled' values to timestamp objects, not strings.
(org-element-clock-interpreter, org-element-planning-interpreter)
(org-element-timestamp-interpreter): Update interpreters.
(org-element--current-element): Tiny refactoring.
* testing/lisp/test-org-element.el: Add tests.
Nicolas Goaziou 12 years ago
parent
commit
c1e3aaece3
2 changed files with 374 additions and 152 deletions
  1. 261 94
      lisp/org-element.el
  2. 113 58
      testing/lisp/test-org-element.el

+ 261 - 94
lisp/org-element.el

@@ -116,6 +116,8 @@
 (eval-when-compile (require 'cl))
 (eval-when-compile (require 'cl))
 (require 'org)
 (require 'org)
 
 
+(declare-function org-clocking-buffer "org-clock" ())
+
 
 
 
 
 ;;; Definitions And Rules
 ;;; Definitions And Rules
@@ -711,9 +713,8 @@ Return a list whose CAR is `headline' and CDR is a plist
 containing `:raw-value', `:title', `:begin', `:end',
 containing `:raw-value', `:title', `:begin', `:end',
 `:pre-blank', `:hiddenp', `:contents-begin' and `:contents-end',
 `:pre-blank', `:hiddenp', `:contents-begin' and `:contents-end',
 `:level', `:priority', `:tags', `:todo-keyword',`:todo-type',
 `:level', `:priority', `:tags', `:todo-keyword',`:todo-type',
-`:scheduled', `:deadline', `:timestamp', `:clock', `:category',
-`:quotedp', `:archivedp', `:commentedp' and `:footnote-section-p'
-keywords.
+`:scheduled', `:deadline', `:closed', `:clockedp', `:quotedp',
+`:archivedp', `:commentedp' and `:footnote-section-p' keywords.
 
 
 The plist also contains any property set in the property drawer,
 The plist also contains any property set in the property drawer,
 with its name in lowercase, the underscores replaced with hyphens
 with its name in lowercase, the underscores replaced with hyphens
@@ -745,23 +746,36 @@ Assume point is at beginning of the headline."
 				    (string= org-footnote-section raw-value)))
 				    (string= org-footnote-section raw-value)))
 	   ;; Normalize property names: ":SOME_PROP:" becomes
 	   ;; Normalize property names: ":SOME_PROP:" becomes
 	   ;; ":some-prop".
 	   ;; ":some-prop".
-	   (standard-props (let (plist)
-			     (mapc
-			      (lambda (p)
-				(let ((p-name (downcase (car p))))
-				  (while (string-match "_" p-name)
-				    (setq p-name
-					  (replace-match "-" nil nil p-name)))
-				  (setq p-name (intern (concat ":" p-name)))
-				  (setq plist
-					(plist-put plist p-name (cdr p)))))
-			      (org-entry-properties nil 'standard))
-			     plist))
-	   (time-props (org-entry-properties nil 'special "CLOCK"))
-	   (scheduled (cdr (assoc "SCHEDULED" time-props)))
-	   (deadline (cdr (assoc "DEADLINE" time-props)))
-	   (clock (cdr (assoc "CLOCK" time-props)))
-	   (timestamp (cdr (assoc "TIMESTAMP" time-props)))
+	   (standard-props
+	    (let (plist)
+	      (mapc
+	       (lambda (p)
+		 (setq plist
+		       (plist-put plist
+				  (intern (concat ":"
+						  (replace-regexp-in-string
+						   "_" "-" (downcase (car p)))))
+				  (cdr p))))
+	       (org-entry-properties nil 'standard))
+	      plist))
+	   (time-props
+	    ;; Read time properties on the line below the headline.
+	    (save-excursion
+	      (when (progn (forward-line)
+			   (looking-at org-planning-or-clock-line-re))
+		(let ((end (line-end-position)) plist)
+		  (while (re-search-forward
+			  org-keyword-time-not-clock-regexp end t)
+		    (goto-char (match-end 1))
+		    (skip-chars-forward " \t")
+		    (let ((keyword (match-string 1))
+			  (time (org-element-timestamp-parser)))
+		      (cond ((equal keyword org-scheduled-string)
+			     (setq plist (plist-put plist :scheduled time)))
+			    ((equal keyword org-deadline-string)
+			     (setq plist (plist-put plist :deadline time)))
+			    (t (setq plist (plist-put plist :closed time))))))
+		  plist))))
 	   (begin (point))
 	   (begin (point))
 	   (end (save-excursion (goto-char (org-end-of-subtree t t))))
 	   (end (save-excursion (goto-char (org-end-of-subtree t t))))
 	   (pos-after-head (progn (forward-line) (point)))
 	   (pos-after-head (progn (forward-line) (point)))
@@ -773,7 +787,13 @@ Assume point is at beginning of the headline."
 			      (progn (goto-char end)
 			      (progn (goto-char end)
 				     (skip-chars-backward " \r\t\n")
 				     (skip-chars-backward " \r\t\n")
 				     (forward-line)
 				     (forward-line)
-				     (point)))))
+				     (point))))
+	   (clockedp (and (eq (org-clocking-buffer)
+			      (or (buffer-base-buffer) (current-buffer)))
+			  (save-excursion
+			    (goto-char (marker-position org-clock-marker))
+			    (org-back-to-heading t)
+			    (= (point) begin)))))
       ;; Clean RAW-VALUE from any quote or comment string.
       ;; Clean RAW-VALUE from any quote or comment string.
       (when (or quotedp commentedp)
       (when (or quotedp commentedp)
 	(let ((case-fold-search nil))
 	(let ((case-fold-search nil))
@@ -803,10 +823,6 @@ Assume point is at beginning of the headline."
 			  :tags tags
 			  :tags tags
 			  :todo-keyword todo
 			  :todo-keyword todo
 			  :todo-type todo-type
 			  :todo-type todo-type
-			  :scheduled scheduled
-			  :deadline deadline
-			  :timestamp timestamp
-			  :clock clock
 			  :post-blank (count-lines
 			  :post-blank (count-lines
 				       (if (not contents-end) pos-after-head
 				       (if (not contents-end) pos-after-head
 					 (goto-char contents-end)
 					 (goto-char contents-end)
@@ -815,8 +831,10 @@ Assume point is at beginning of the headline."
 				       end)
 				       end)
 			  :footnote-section-p footnote-section-p
 			  :footnote-section-p footnote-section-p
 			  :archivedp archivedp
 			  :archivedp archivedp
+			  :clockedp clockedp
 			  :commentedp commentedp
 			  :commentedp commentedp
 			  :quotedp quotedp)
 			  :quotedp quotedp)
+		    time-props
 		    standard-props))))
 		    standard-props))))
 	(org-element-put-property
 	(org-element-put-property
 	 headline :title
 	 headline :title
@@ -880,7 +898,7 @@ Return a list whose CAR is `inlinetask' and CDR is a plist
 containing `:title', `:begin', `:end', `:hiddenp',
 containing `:title', `:begin', `:end', `:hiddenp',
 `:contents-begin' and `:contents-end', `:level', `:priority',
 `:contents-begin' and `:contents-end', `:level', `:priority',
 `:raw-value', `:tags', `:todo-keyword', `:todo-type',
 `:raw-value', `:tags', `:todo-keyword', `:todo-type',
-`:scheduled', `:deadline', `:timestamp', `:clock' and
+`:scheduled', `:deadline', `:clockedp', `:closed' and
 `:post-blank' keywords.
 `:post-blank' keywords.
 
 
 The plist also contains any property set in the property drawer,
 The plist also contains any property set in the property drawer,
@@ -904,27 +922,45 @@ Assume point is at beginning of the inline task."
 	   (raw-value (or (nth 4 components) ""))
 	   (raw-value (or (nth 4 components) ""))
 	   ;; Normalize property names: ":SOME_PROP:" becomes
 	   ;; Normalize property names: ":SOME_PROP:" becomes
 	   ;; ":some-prop".
 	   ;; ":some-prop".
-	   (standard-props (let (plist)
-			     (mapc
-			      (lambda (p)
-				(let ((p-name (downcase (car p))))
-				  (while (string-match "_" p-name)
-				    (setq p-name
-					  (replace-match "-" nil nil p-name)))
-				  (setq p-name (intern (concat ":" p-name)))
-				  (setq plist
-					(plist-put plist p-name (cdr p)))))
-			      (org-entry-properties nil 'standard))
-			     plist))
-	   (time-props (org-entry-properties nil 'special "CLOCK"))
-	   (scheduled (cdr (assoc "SCHEDULED" time-props)))
-	   (deadline (cdr (assoc "DEADLINE" time-props)))
-	   (clock (cdr (assoc "CLOCK" time-props)))
-	   (timestamp (cdr (assoc "TIMESTAMP" time-props)))
+	   (standard-props
+	    (let (plist)
+	      (mapc
+	       (lambda (p)
+		 (setq plist
+		       (plist-put plist
+				  (intern (concat ":"
+						  (replace-regexp-in-string
+						   "_" "-" (downcase (car p)))))
+				  (cdr p))))
+	       (org-entry-properties nil 'standard))
+	      plist))
+	   (time-props
+	    ;; Read time properties on the line below the inlinetask
+	    ;; opening string.
+	    (save-excursion
+	      (when (progn (forward-line)
+			   (looking-at org-planning-or-clock-line-re))
+		(let ((end (line-end-position)) plist)
+		  (while (re-search-forward
+			  org-keyword-time-not-clock-regexp end t)
+		    (goto-char (match-end 1))
+		    (skip-chars-forward " \t")
+		    (let ((keyword (match-string 1))
+			  (time (org-element-timestamp-parser)))
+		      (cond ((equal keyword org-scheduled-string)
+			     (setq plist (plist-put plist :scheduled time)))
+			    ((equal keyword org-deadline-string)
+			     (setq plist (plist-put plist :deadline time)))
+			    (t (setq plist (plist-put plist :closed time))))))
+		  plist))))
 	   (task-end (save-excursion
 	   (task-end (save-excursion
 		       (end-of-line)
 		       (end-of-line)
 		       (and (re-search-forward "^\\*+ END" limit t)
 		       (and (re-search-forward "^\\*+ END" limit t)
 			    (match-beginning 0))))
 			    (match-beginning 0))))
+	   (clockedp (and (eq (org-clocking-buffer)
+			      (or (buffer-base-buffer) (current-buffer)))
+			  (let ((clock (marker-position org-clock-marker)))
+			    (and (> clock begin) (< clock task-end)))))
 	   (contents-begin (progn (forward-line)
 	   (contents-begin (progn (forward-line)
 				  (and task-end (< (point) task-end) (point))))
 				  (and task-end (< (point) task-end) (point))))
 	   (hidden (and contents-begin (org-invisible-p2)))
 	   (hidden (and contents-begin (org-invisible-p2)))
@@ -950,11 +986,9 @@ Assume point is at beginning of the inline task."
 			 :tags tags
 			 :tags tags
 			 :todo-keyword todo
 			 :todo-keyword todo
 			 :todo-type todo-type
 			 :todo-type todo-type
-			 :scheduled scheduled
-			 :deadline deadline
-			 :timestamp timestamp
-			 :clock clock
+			 :clockedp clockedp
 			 :post-blank (count-lines before-blank end))
 			 :post-blank (count-lines before-blank end))
+		   time-props
 		   standard-props
 		   standard-props
 		   (cadr keywords)))))
 		   (cadr keywords)))))
       (org-element-put-property
       (org-element-put-property
@@ -1427,13 +1461,13 @@ as keywords."
     (let* ((case-fold-search nil)
     (let* ((case-fold-search nil)
 	   (begin (point))
 	   (begin (point))
 	   (value (progn (search-forward org-clock-string (line-end-position) t)
 	   (value (progn (search-forward org-clock-string (line-end-position) t)
-			 (org-skip-whitespace)
-			 (looking-at "\\[.*\\]")
-			 (org-match-string-no-properties 0)))
-	   (time (and (progn (goto-char (match-end 0))
-			     (looking-at " +=> +\\(\\S-+\\)[ \t]*$"))
-		      (org-match-string-no-properties 1)))
-	   (status (if time 'closed 'running))
+			 (skip-chars-forward " \t")
+			 (org-element-timestamp-parser)))
+	   (duration (and (search-forward " => " (line-end-position) t)
+			  (progn (skip-chars-forward " \t")
+				 (looking-at "\\(\\S-+\\)[ \t]*$"))
+			  (org-match-string-no-properties 1)))
+	   (status (if duration 'closed 'running))
 	   (post-blank (let ((before-blank (progn (forward-line) (point))))
 	   (post-blank (let ((before-blank (progn (forward-line) (point))))
 			 (skip-chars-forward " \r\t\n" limit)
 			 (skip-chars-forward " \r\t\n" limit)
 			 (skip-chars-backward " \t")
 			 (skip-chars-backward " \t")
@@ -1443,7 +1477,7 @@ as keywords."
       (list 'clock
       (list 'clock
 	    (list :status status
 	    (list :status status
 		  :value value
 		  :value value
-		  :time time
+		  :duration duration
 		  :begin begin
 		  :begin begin
 		  :end end
 		  :end end
 		  :post-blank post-blank)))))
 		  :post-blank post-blank)))))
@@ -1452,13 +1486,14 @@ as keywords."
   "Interpret CLOCK element as Org syntax.
   "Interpret CLOCK element as Org syntax.
 CONTENTS is nil."
 CONTENTS is nil."
   (concat org-clock-string " "
   (concat org-clock-string " "
-	  (org-element-property :value clock)
-	  (let ((time (org-element-property :time clock)))
-	    (and time
+	  (org-element-timestamp-interpreter
+	   (org-element-property :value clock) nil)
+	  (let ((duration (org-element-property :duration clock)))
+	    (and duration
 		 (concat " => "
 		 (concat " => "
 			 (apply 'format
 			 (apply 'format
 				"%2s:%02s"
 				"%2s:%02s"
-				(org-split-string time ":")))))))
+				(org-split-string duration ":")))))))
 
 
 
 
 ;;;; Comment
 ;;;; Comment
@@ -2049,13 +2084,11 @@ and `:post-blank' keywords."
 	   (end (point))
 	   (end (point))
 	   closed deadline scheduled)
 	   closed deadline scheduled)
       (goto-char begin)
       (goto-char begin)
-      (while (re-search-forward org-keyword-time-not-clock-regexp
-				(line-end-position) t)
+      (while (re-search-forward org-keyword-time-not-clock-regexp end t)
 	(goto-char (match-end 1))
 	(goto-char (match-end 1))
-	(org-skip-whitespace)
-	(let ((time (buffer-substring-no-properties
-		     (1+ (point)) (1- (match-end 0))))
-	      (keyword (match-string 1)))
+	(skip-chars-forward " \t" end)
+	(let ((keyword (match-string 1))
+	      (time (org-element-timestamp-parser)))
 	  (cond ((equal keyword org-closed-string) (setq closed time))
 	  (cond ((equal keyword org-closed-string) (setq closed time))
 		((equal keyword org-deadline-string) (setq deadline time))
 		((equal keyword org-deadline-string) (setq deadline time))
 		(t (setq scheduled time)))))
 		(t (setq scheduled time)))))
@@ -2073,13 +2106,18 @@ CONTENTS is nil."
   (mapconcat
   (mapconcat
    'identity
    'identity
    (delq nil
    (delq nil
-	 (list (let ((closed (org-element-property :closed planning)))
-		 (when closed (concat org-closed-string " [" closed "]")))
-	       (let ((deadline (org-element-property :deadline planning)))
-		 (when deadline (concat org-deadline-string " <" deadline ">")))
+	 (list (let ((deadline (org-element-property :deadline planning)))
+		 (when deadline
+		   (concat org-deadline-string " "
+			   (org-element-timestamp-interpreter deadline nil))))
 	       (let ((scheduled (org-element-property :scheduled planning)))
 	       (let ((scheduled (org-element-property :scheduled planning)))
 		 (when scheduled
 		 (when scheduled
-		   (concat org-scheduled-string " <" scheduled ">")))))
+		   (concat org-scheduled-string " "
+			   (org-element-timestamp-interpreter scheduled nil))))
+	       (let ((closed (org-element-property :closed planning)))
+		 (when closed
+		   (concat org-closed-string " "
+			   (org-element-timestamp-interpreter closed nil))))))
    " "))
    " "))
 
 
 
 
@@ -3358,39 +3396,168 @@ Assume point is at the beginning of the timestamp."
   (save-excursion
   (save-excursion
     (let* ((begin (point))
     (let* ((begin (point))
 	   (activep (eq (char-after) ?<))
 	   (activep (eq (char-after) ?<))
-	   (main-value
+	   (raw-value
 	    (progn
 	    (progn
-	      (looking-at "[<[]\\(\\(%%\\)?.*?\\)[]>]\\(?:--[<[]\\(.*?\\)[]>]\\)?")
-	      (match-string-no-properties 1)))
-	   (range-end (match-string-no-properties 3))
-	   (type (cond ((match-string 2) 'diary)
-		       ((and activep range-end) 'active-range)
+	      (looking-at "\\([<[]\\(%%\\)?.*?\\)[]>]\\(?:--\\([<[].*?[]>]\\)\\)?")
+	      (match-string-no-properties 0)))
+	   (date-start (match-string-no-properties 1))
+	   (date-end (match-string 3))
+	   (diaryp (match-beginning 2))
+	   (type (cond (diaryp 'diary)
+		       ((and activep date-end) 'active-range)
 		       (activep 'active)
 		       (activep 'active)
-		       (range-end 'inactive-range)
+		       (date-end 'inactive-range)
 		       (t 'inactive)))
 		       (t 'inactive)))
 	   (post-blank (progn (goto-char (match-end 0))
 	   (post-blank (progn (goto-char (match-end 0))
 			      (skip-chars-forward " \t")))
 			      (skip-chars-forward " \t")))
-	   (end (point)))
+	   (end (point))
+	   (with-time-p (string-match "[012]?[0-9]:[0-5][0-9]" date-start))
+	   (repeater-props
+	    (and (not diaryp)
+		 (string-match "\\([.+]?\\+\\)\\([0-9]+\\)\\([hdwmy]\\)>"
+			       raw-value)
+		 (list
+		  :repeater-type
+		  (let ((type (match-string 1 raw-value)))
+		    (cond ((equal "++" type) 'catch-up)
+			  ((equal ".+" type) 'restart)
+			  (t 'cumulate)))
+		  :repeater-value (string-to-number (match-string 2 raw-value))
+		  :repeater-unit
+		  (case (string-to-char (match-string 3 raw-value))
+		    (?h 'hour) (?d 'day) (?w 'week) (?m 'month) (t 'year)))))
+	   time-range year-start month-start day-start hour-start minute-start
+	   year-end month-end day-end hour-end minute-end)
+      ;; Extract time range, if any, and remove it from date start.
+      (setq time-range
+	    (and (not diaryp)
+		 (string-match
+		  "[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
+		  date-start)
+		 (cons (string-to-number (match-string 2 date-start))
+		       (string-to-number (match-string 3 date-start)))))
+      (when time-range
+	(setq date-start (replace-match "" nil nil date-start 1)))
+      ;; Parse date-start.
+      (unless diaryp
+	(let ((date (org-parse-time-string date-start)))
+	  (setq year-start (nth 5 date)
+		month-start (nth 4 date)
+		day-start (nth 3 date)
+		hour-start (and with-time-p (nth 2 date))
+		minute-start (and with-time-p (nth 1 date)))))
+      ;; Compute date-end.  It can be provided directly in time-stamp,
+      ;; or extracted from time range.  Otherwise, it defaults to the
+      ;; same values as date-start.
+      (unless diaryp
+	(let ((date (and date-end (org-parse-time-string date-end))))
+	  (setq year-end (or (nth 5 date) year-start)
+		month-end (or (nth 4 date) month-start)
+		day-end (or (nth 3 date) day-start)
+		hour-end (or (nth 2 date) (car time-range) hour-start)
+		minute-end (or (nth 1 date) (cdr time-range) minute-start))))
       (list 'timestamp
       (list 'timestamp
-	    (list :type type
-		  :value main-value
-		  :range-end range-end
-		  :begin begin
-		  :end end
-		  :post-blank post-blank)))))
+	    (nconc (list :type type
+			 :raw-value raw-value
+			 :year-start year-start
+			 :month-start month-start
+			 :day-start day-start
+			 :hour-start hour-start
+			 :minute-start minute-start
+			 :year-end year-end
+			 :month-end month-end
+			 :day-end day-end
+			 :hour-end hour-end
+			 :minute-end minute-end
+			 :begin begin
+			 :end end
+			 :post-blank post-blank)
+		   repeater-props)))))
 
 
 (defun org-element-timestamp-interpreter (timestamp contents)
 (defun org-element-timestamp-interpreter (timestamp contents)
   "Interpret TIMESTAMP object as Org syntax.
   "Interpret TIMESTAMP object as Org syntax.
 CONTENTS is nil."
 CONTENTS is nil."
-  (let ((type (org-element-property :type timestamp) ))
-    (concat
-     (format (if (memq type '(inactive inactive-range)) "[%s]" "<%s>")
-	     (org-element-property :value timestamp))
-     (let ((range-end (org-element-property :range-end timestamp)))
-       (when range-end
-	 (concat "--"
-		 (format (if (eq type 'inactive-range) "[%s]" "<%s>")
-			 range-end)))))))
+  ;; Use `:raw-value' if specified.
+  (or (org-element-property :raw-value timestamp)
+      ;; Otherwise, build timestamp string.
+      (let ((build-ts-string
+	     ;; Build an Org timestamp string from TIME.  ACTIVEP is
+	     ;; non-nil when time stamp is active.  If WITH-TIME-P is
+	     ;; non-nil, add a time part.  HOUR-END and MINUTE-END
+	     ;; specify a time range in the timestamp.  REPEAT-STRING
+	     ;; is the repeater string, if any.
+	     (lambda (time activep
+		      &optional with-time-p hour-end minute-end repeat-string)
+	       (let ((ts (format-time-string
+			  (funcall (if with-time-p 'cdr 'car)
+				   org-time-stamp-formats)
+			  time)))
+		 (when (and hour-end minute-end)
+		   (string-match "[012]?[0-9]:[0-5][0-9]" ts)
+		   (setq ts
+			 (replace-match
+			  (format "\\&-%02d:%02d" hour-end minute-end)
+			  nil nil ts)))
+		 (unless activep (setq ts (format "[%s]" (substring ts 1 -1))))
+		 (when (org-string-nw-p repeat-string)
+		   (setq ts (concat (substring ts 0 -1)
+				    " "
+				    repeat-string
+				    (substring ts -1))))
+		 ;; Return value.
+		 ts)))
+	    (type (org-element-property :type timestamp)))
+	(case type
+	  ((active inactive)
+	   (let* ((minute-start (org-element-property :minute-start timestamp))
+		  (minute-end (org-element-property :minute-end timestamp))
+		  (hour-start (org-element-property :hour-start timestamp))
+		  (hour-end (org-element-property :hour-end timestamp))
+		  (time-range-p (and hour-start hour-end minute-start minute-end
+				     (or (/= hour-start hour-end)
+					 (/= minute-start minute-end)))))
+	     (funcall
+	      build-ts-string
+	      (encode-time 0
+			   (or minute-start 0)
+			   (or hour-start 0)
+			   (org-element-property :day-start timestamp)
+			   (org-element-property :month-start timestamp)
+			   (org-element-property :year-start timestamp))
+	      (eq type 'active)
+	      (and hour-start minute-start)
+	      (and time-range-p hour-end)
+	      (and time-range-p minute-end)
+	      (concat (case (org-element-property :repeater-type timestamp)
+			(cumulate "+") (catch-up "++") (restart ".+"))
+		      (org-element-property :repeater-value timestamp)
+		      (org-element-property :repeater-unit timestamp)))))
+	  ((active-range inactive-range)
+	   (let ((minute-start (org-element-property :minute-start timestamp))
+		 (minute-end (org-element-property :minute-end timestamp))
+		 (hour-start (org-element-property :hour-start timestamp))
+		 (hour-end (org-element-property :hour-end timestamp)))
+	     (concat
+	      (funcall
+	       build-ts-string (encode-time
+				0
+				(or minute-start 0)
+				(or hour-start 0)
+				(org-element-property :day-start timestamp)
+				(org-element-property :month-start timestamp)
+				(org-element-property :year-start timestamp))
+	       (eq type 'active-range)
+	       (and hour-start minute-start))
+	      "--"
+	      (funcall build-ts-string
+		       (encode-time 0
+				    (or minute-end 0)
+				    (or hour-end 0)
+				    (org-element-property :day-end timestamp)
+				    (org-element-property :month-end timestamp)
+				    (org-element-property :year-end timestamp))
+		       (eq type 'active-range)
+		       (and hour-end minute-end)))))))))
 
 
 (defun org-element-timestamp-successor (limit)
 (defun org-element-timestamp-successor (limit)
   "Search for the next timestamp object.
   "Search for the next timestamp object.
@@ -3539,7 +3706,7 @@ element it has to parse."
        ;; a footnote definition: next item is always a paragraph.
        ;; a footnote definition: next item is always a paragraph.
        ((not (bolp)) (org-element-paragraph-parser limit (list (point))))
        ((not (bolp)) (org-element-paragraph-parser limit (list (point))))
        ;; Planning and Clock.
        ;; Planning and Clock.
-       ((and (looking-at org-planning-or-clock-line-re))
+       ((looking-at org-planning-or-clock-line-re)
 	(if (equal (match-string 1) org-clock-string)
 	(if (equal (match-string 1) org-clock-string)
 	    (org-element-clock-parser limit)
 	    (org-element-clock-parser limit)
 	  (org-element-planning-parser limit)))
 	  (org-element-planning-parser limit)))

+ 113 - 58
testing/lisp/test-org-element.el

@@ -278,22 +278,24 @@ Some other text
   ;; Running clock.
   ;; Running clock.
   (let* ((org-clock-string "CLOCK:")
   (let* ((org-clock-string "CLOCK:")
 	 (clock (org-test-with-temp-text "CLOCK: [2012-01-01 sun. 00:01]"
 	 (clock (org-test-with-temp-text "CLOCK: [2012-01-01 sun. 00:01]"
-		  (org-element-map
-		   (org-element-parse-buffer) 'clock 'identity nil t))))
+		  (org-element-at-point))))
     (should (eq (org-element-property :status clock) 'running))
     (should (eq (org-element-property :status clock) 'running))
-    (should (equal (org-element-property :value clock)
-		   "[2012-01-01 sun. 00:01]"))
-    (should-not (org-element-property :time clock)))
+    (should
+     (equal (org-element-property :raw-value
+				  (org-element-property :value clock))
+	    "[2012-01-01 sun. 00:01]"))
+    (should-not (org-element-property :duration clock)))
   ;; Closed clock.
   ;; Closed clock.
   (let* ((org-clock-string "CLOCK:")
   (let* ((org-clock-string "CLOCK:")
-	 (clock (org-test-with-temp-text "
-CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"
-		  (org-element-map
-		   (org-element-parse-buffer) 'clock 'identity nil t))))
+	 (clock
+	  (org-test-with-temp-text
+	      "CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"
+	    (org-element-at-point))))
     (should (eq (org-element-property :status clock) 'closed))
     (should (eq (org-element-property :status clock) 'closed))
-    (should (equal (org-element-property :value clock)
+    (should (equal (org-element-property :raw-value
+					 (org-element-property :value clock))
 		   "[2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02]"))
 		   "[2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02]"))
-    (should (equal (org-element-property :time clock) "0:01"))))
+    (should (equal (org-element-property :duration clock) "0:01"))))
 
 
 
 
 ;;;; Code
 ;;;; Code
@@ -1382,26 +1384,29 @@ Outside list"
 (ert-deftest test-org-element/planning-parser ()
 (ert-deftest test-org-element/planning-parser ()
   "Test `planning' parser."
   "Test `planning' parser."
   (should
   (should
-   (equal
-    (org-element-property
-     :closed
-     (org-test-with-temp-text "CLOSED: [2012-03-29 thu.]"
-       (org-element-map (org-element-parse-buffer) 'planning 'identity nil t)))
-    "2012-03-29 thu."))
+   (equal "[2012-03-29 thu.]"
+	  (org-element-property
+	   :raw-value
+	   (org-element-property
+	    :closed
+	    (org-test-with-temp-text "CLOSED: [2012-03-29 thu.]"
+	      (org-element-at-point))))))
   (should
   (should
-   (equal
-    (org-element-property
-     :deadline
-     (org-test-with-temp-text "DEADLINE: <2012-03-29 thu.>"
-       (org-element-map (org-element-parse-buffer) 'planning 'identity nil t)))
-    "2012-03-29 thu."))
+   (equal "<2012-03-29 thu.>"
+	  (org-element-property
+	   :raw-value
+	   (org-element-property
+	    :deadline
+	    (org-test-with-temp-text "DEADLINE: <2012-03-29 thu.>"
+	      (org-element-at-point))))))
   (should
   (should
-   (equal
-    (org-element-property
-     :scheduled
-     (org-test-with-temp-text "SCHEDULED: <2012-03-29 thu.>"
-       (org-element-map (org-element-parse-buffer) 'planning 'identity nil t)))
-    "2012-03-29 thu.")))
+   (equal "<2012-03-29 thu.>"
+	  (org-element-property
+	   :raw-value
+	   (org-element-property
+	    :scheduled
+	    (org-test-with-temp-text "SCHEDULED: <2012-03-29 thu.>"
+	      (org-element-at-point)))))))
 
 
 
 
 ;;;; Property Drawer
 ;;;; Property Drawer
@@ -1707,27 +1712,41 @@ Outside list"
   ;; Active timestamp.
   ;; Active timestamp.
   (should
   (should
    (org-test-with-temp-text "<2012-03-29 16:40>"
    (org-test-with-temp-text "<2012-03-29 16:40>"
-     (eq (org-element-property :type
-			       (org-element-map
-				(org-element-parse-buffer)
-				'timestamp 'identity nil t))
-	 'active)))
+     (eq (org-element-property :type (org-element-context)) 'active)))
+  (should
+   (equal '(2012 3 29 16 40)
+	  (org-test-with-temp-text "<2012-03-29 16:40>"
+	    (let ((object (org-element-context)))
+	      (list (org-element-property :year-start object)
+		    (org-element-property :month-start object)
+		    (org-element-property :day-start object)
+		    (org-element-property :hour-start object)
+		    (org-element-property :minute-start object))))))
   ;; Inactive timestamp.
   ;; Inactive timestamp.
   (should
   (should
    (org-test-with-temp-text "[2012-03-29 16:40]"
    (org-test-with-temp-text "[2012-03-29 16:40]"
-     (eq (org-element-property :type
-			       (org-element-map
-				(org-element-parse-buffer)
-				'timestamp 'identity nil t))
-	 'inactive)))
+     (eq (org-element-property :type (org-element-context)) 'inactive)))
+  ;; Time range.
+  (should
+   (equal '(2012 3 29 16 40 7 30)
+	  (org-test-with-temp-text "<2012-03-29 7:30-16:40>"
+	    (let ((object (org-element-context)))
+	      (list (org-element-property :year-end object)
+		    (org-element-property :month-end object)
+		    (org-element-property :day-end object)
+		    (org-element-property :hour-end object)
+		    (org-element-property :minute-end object)
+		    (org-element-property :hour-start object)
+		    (org-element-property :minute-start object))))))
   ;; Date range.
   ;; Date range.
   (should
   (should
    (org-test-with-temp-text "[2012-03-29 16:40]--[2012-03-29 16:41]"
    (org-test-with-temp-text "[2012-03-29 16:40]--[2012-03-29 16:41]"
-     (eq (org-element-property :type
-			       (org-element-map
-				(org-element-parse-buffer)
-				'timestamp 'identity nil t))
-	 'inactive-range)))
+     (eq (org-element-property :type (org-element-context)) 'inactive-range)))
+  ;; With repeater.
+  (should
+   (eq 'catch-up
+       (org-test-with-temp-text "<2012-03-29 ++1y>"
+	 (org-element-property :repeater-type (org-element-context)))))
   ;; Timestamps are not planning elements.
   ;; Timestamps are not planning elements.
   (should-not
   (should-not
    (org-test-with-temp-text "SCHEDULED: <2012-03-29 16:40>"
    (org-test-with-temp-text "SCHEDULED: <2012-03-29 16:40>"
@@ -2102,9 +2121,9 @@ CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"))
      (equal
      (equal
       (org-test-parse-and-interpret
       (org-test-parse-and-interpret
        "* Headline
        "* Headline
-CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>")
+DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01> CLOSED: [2012-01-01]")
       "* Headline
       "* Headline
-CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>\n"))))
+DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01> CLOSED: [2012-01-01]\n"))))
 
 
 (ert-deftest test-org-element/property-drawer-interpreter ()
 (ert-deftest test-org-element/property-drawer-interpreter ()
   "Test property drawer interpreter."
   "Test property drawer interpreter."
@@ -2159,25 +2178,61 @@ CLOSED: [2012-01-01] DEADLINE: <2012-01-01> SCHEDULED: <2012-01-01>\n"))))
 (ert-deftest test-org-element/timestamp-interpreter ()
 (ert-deftest test-org-element/timestamp-interpreter ()
   "Test timestamp interpreter."
   "Test timestamp interpreter."
   ;; Active.
   ;; Active.
-  (should (equal (org-test-parse-and-interpret "<2012-03-29 16:40>")
-		 "<2012-03-29 16:40>\n"))
+  (should (equal (org-test-parse-and-interpret "<2012-03-29 thu. 16:40>")
+		 "<2012-03-29 thu. 16:40>\n"))
+  (should
+   (string-match "<2012-03-29 .* 16:40>"
+		 (org-element-timestamp-interpreter
+		  '(timestamp
+		    (:type active :year-start 2012 :month-start 3 :day-start 29
+			   :hour-start 16 :minute-start 40)) nil)))
   ;; Inactive.
   ;; Inactive.
-  (should (equal (org-test-parse-and-interpret "[2012-03-29 16:40]")
-		 "[2012-03-29 16:40]\n"))
+  (should (equal (org-test-parse-and-interpret "[2012-03-29 thu. 16:40]")
+		 "[2012-03-29 thu. 16:40]\n"))
+  (should
+   (string-match
+    "\\[2012-03-29 .* 16:40\\]"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type inactive :year-start 2012 :month-start 3 :day-start 29
+	      :hour-start 16 :minute-start 40)) nil)))
   ;; Active range.
   ;; Active range.
   (should (equal (org-test-parse-and-interpret
   (should (equal (org-test-parse-and-interpret
-		  "<2012-03-29 16:40>--<2012-03-29 16:41>")
-		 "<2012-03-29 16:40>--<2012-03-29 16:41>\n"))
+		  "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>")
+		 "<2012-03-29 thu. 16:40>--<2012-03-29 thu. 16:41>\n"))
+  (should
+   (string-match
+    "<2012-03-29 .* 16:40>--<2012-03-29 .* 16:41>"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type active-range :year-start 2012 :month-start 3 :day-start 29
+	      :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3
+	      :day-end 29 :hour-end 16 :minute-end 41)) nil)))
   ;; Inactive range.
   ;; Inactive range.
   (should (equal (org-test-parse-and-interpret
   (should (equal (org-test-parse-and-interpret
-		  "[2012-03-29 16:40]--[2012-03-29 16:41]")
-		 "[2012-03-29 16:40]--[2012-03-29 16:41]\n"))
+		  "[2012-03-29 thu. 16:40]--[2012-03-29 thu. 16:41]")
+		 "[2012-03-29 thu. 16:40]--[2012-03-29 thu. 16:41]\n"))
+  (should
+   (string-match
+    "\\[2012-03-29 .* 16:40\\]--\\[2012-03-29 .* 16:41\\]"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type inactive-range :year-start 2012 :month-start 3 :day-start 29
+	      :hour-start 16 :minute-start 40 :year-end 2012 :month-end 3
+	      :day-end 29 :hour-end 16 :minute-end 41)) nil)))
   ;; Diary.
   ;; Diary.
-  (should (equal (org-test-parse-and-interpret "<%%org-float t 4 2>")
-		 "<%%org-float t 4 2>\n"))
+  (should (equal (org-test-parse-and-interpret "<%%(org-float t 4 2)>")
+		 "<%%(org-float t 4 2)>\n"))
   ;; Timestamp with repeater interval.
   ;; Timestamp with repeater interval.
-  (should (equal (org-test-parse-and-interpret "<2012-03-29 +1y>")
-		 "<2012-03-29 +1y>\n")))
+  (should (equal (org-test-parse-and-interpret "<2012-03-29 thu. +1y>")
+		 "<2012-03-29 thu. +1y>\n"))
+  (should
+   (string-match
+    "<2012-03-29 .* \\+1y>"
+    (org-element-timestamp-interpreter
+     '(timestamp
+       (:type active :year-start 2012 :month-start 3 :day-start 29
+	      :repeater-type cumulate :repeater-value "1y")) nil))))
 
 
 (ert-deftest test-org-element/verse-block-interpreter ()
 (ert-deftest test-org-element/verse-block-interpreter ()
   "Test verse block interpretation."
   "Test verse block interpretation."