org-element: Properly parse headline properties

* lisp/org-element.el (org-element-headline-parser,
  org-element-inlinetask-parser): First find appropriate property
  drawer in order to read properties.

* testing/lisp/test-org-element.el (test-org-element/headline-properties):
  Add tests.

Thanks to Sébastien Vauban for reporting it.
http://permalink.gmane.org/gmane.emacs.orgmode/87701
This commit is contained in:
Nicolas Goaziou 2014-06-18 23:28:19 +02:00
parent b1dcd0fb0b
commit ab145f0df5
2 changed files with 113 additions and 47 deletions

View File

@ -811,24 +811,48 @@ Assume point is at beginning of the headline."
(archivedp (member org-archive-tag tags)) (archivedp (member org-archive-tag tags))
(footnote-section-p (and org-footnote-section (footnote-section-p (and org-footnote-section
(string= org-footnote-section raw-value))) (string= org-footnote-section raw-value)))
;; Upcase property names. It avoids confusion between
;; properties obtained through property drawer and default
;; properties from the parser (e.g. `:end' and :END:)
(standard-props (standard-props
(let (plist) ;; Find property drawer associated to current headline and
(mapc ;; extract properties.
(lambda (p) ;;
(setq plist ;; Upcase property names. It avoids confusion between
(plist-put plist ;; properties obtained through property drawer and default
(intern (concat ":" (upcase (car p)))) ;; properties from the parser (e.g. `:end' and :END:)
(cdr p)))) (let ((end (save-excursion
(org-entry-properties nil 'standard)) (org-with-limited-levels (outline-next-heading))
plist)) (point)))
plist)
(save-excursion
(while (and (null plist)
(re-search-forward org-property-start-re end t))
(let ((drawer (org-element-at-point)))
(when (and (eq (org-element-type drawer) 'property-drawer)
;; Make sure drawer is not associated
;; to an inlinetask.
(let ((p drawer))
(while (and (setq p (org-element-property
:parent p))
(not (eq (org-element-type p)
'inlinetask))))
(not p)))
(let ((end (org-element-property :contents-end drawer)))
(when end
(forward-line)
(while (< (point) end)
(looking-at org-property-re)
(setq plist
(plist-put
plist
(intern
(concat ":" (upcase (match-string 2))))
(org-match-string-no-properties 3)))
(forward-line)))))))
plist)))
(time-props (time-props
;; Read time properties on the line below the headline. ;; Read time properties on the line below the headline.
(save-excursion (save-excursion
(when (progn (forward-line) (forward-line)
(looking-at org-planning-or-clock-line-re)) (when (looking-at org-planning-or-clock-line-re)
(let ((end (line-end-position)) plist) (let ((end (line-end-position)) plist)
(while (re-search-forward (while (re-search-forward
org-keyword-time-not-clock-regexp end t) org-keyword-time-not-clock-regexp end t)
@ -974,43 +998,59 @@ Assume point is at beginning of the inline task."
(tags (let ((raw-tags (nth 5 components))) (tags (let ((raw-tags (nth 5 components)))
(and raw-tags (org-split-string raw-tags ":")))) (and raw-tags (org-split-string raw-tags ":"))))
(raw-value (or (nth 4 components) "")) (raw-value (or (nth 4 components) ""))
;; Upcase property names. It avoids confusion between
;; properties obtained through property drawer and default
;; properties from the parser (e.g. `:end' and :END:)
(standard-props
(let (plist)
(mapc
(lambda (p)
(setq plist
(plist-put plist
(intern (concat ":" (upcase (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 org-outline-regexp-bol limit t) (and (re-search-forward org-outline-regexp-bol limit t)
(org-looking-at-p "END[ \t]*$") (org-looking-at-p "END[ \t]*$")
(line-beginning-position)))) (line-beginning-position))))
(standard-props
;; Find property drawer associated to current inlinetask
;; and extract properties.
;;
;; Upcase property names. It avoids confusion between
;; properties obtained through property drawer and default
;; properties from the parser (e.g. `:end' and :END:)
(when task-end
(let (plist)
(save-excursion
(while (and (null plist)
(re-search-forward
org-property-start-re task-end t))
(let ((d (org-element-at-point)))
(when (eq (org-element-type d) 'property-drawer)
(let ((end (org-element-property :contents-end d)))
(when end
(forward-line)
(while (< (point) end)
(looking-at org-property-re)
(setq plist
(plist-put
plist
(intern
(concat ":" (upcase (match-string 2))))
(org-match-string-no-properties 3)))
(forward-line)))))))
plist))))
(time-props
;; Read time properties on the line below the inlinetask
;; opening string.
(when task-end
(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)))))
(contents-begin (progn (forward-line) (contents-begin (progn (forward-line)
(and task-end (< (point) task-end) (point)))) (and task-end (< (point) task-end) (point))))
(contents-end (and contents-begin task-end)) (contents-end (and contents-begin task-end))

View File

@ -1003,7 +1003,33 @@ Some other text
(org-element-property :FOO (org-element-at-point)))) (org-element-property :FOO (org-element-at-point))))
(should-not (should-not
(org-test-with-temp-text "* Headline\n:PROPERTIES:\n:foo: bar\n:END:" (org-test-with-temp-text "* Headline\n:PROPERTIES:\n:foo: bar\n:END:"
(org-element-property :foo (org-element-at-point))))) (org-element-property :foo (org-element-at-point))))
;; Do not find property drawer in a verbatim area.
(should-not
(org-test-with-temp-text
"* Headline
#+BEGIN_EXAMPLE
:PROPERTIES:
:foo: bar
:END:
#+END_EXAMPLE"
(org-element-property :FOO (org-element-at-point))))
;; Do not use properties from a drawer associated to an inlinetask.
(when (featurep 'org-inlinetask)
(should-not
(org-test-with-temp-text
"* Headline
*************** Inlinetask
:PROPERTIES:
:foo: bar
:END:
*************** END"
(org-element-property
:FOO (let ((org-inlinetask-min-level 15)) (org-element-at-point))))))
;; Do not find incomplete drawers.
(should-not
(org-test-with-temp-text "* Headline\n:PROPERTIES:\n:foo: bar"
(org-element-property :FOO (org-element-at-point)))))
;;;; Horizontal Rule ;;;; Horizontal Rule