From ab145f0df553e0bb77795c946ff158d8c069f6e4 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Wed, 18 Jun 2014 23:28:19 +0200 Subject: [PATCH] org-element: Properly parse headline properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- lisp/org-element.el | 132 ++++++++++++++++++++----------- testing/lisp/test-org-element.el | 28 ++++++- 2 files changed, 113 insertions(+), 47 deletions(-) diff --git a/lisp/org-element.el b/lisp/org-element.el index 5af513f56..f0fc09aba 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -811,24 +811,48 @@ Assume point is at beginning of the headline." (archivedp (member org-archive-tag tags)) (footnote-section-p (and org-footnote-section (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 - (let (plist) - (mapc - (lambda (p) - (setq plist - (plist-put plist - (intern (concat ":" (upcase (car p)))) - (cdr p)))) - (org-entry-properties nil 'standard)) - plist)) + ;; Find property drawer associated to current headline 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:) + (let ((end (save-excursion + (org-with-limited-levels (outline-next-heading)) + (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 ;; Read time properties on the line below the headline. (save-excursion - (when (progn (forward-line) - (looking-at org-planning-or-clock-line-re)) + (forward-line) + (when (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) @@ -974,43 +998,59 @@ Assume point is at beginning of the inline task." (tags (let ((raw-tags (nth 5 components))) (and raw-tags (org-split-string raw-tags ":")))) (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 (end-of-line) (and (re-search-forward org-outline-regexp-bol limit t) (org-looking-at-p "END[ \t]*$") (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) (and task-end (< (point) task-end) (point)))) (contents-end (and contents-begin task-end)) diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index d3a5428f4..da45193b4 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -1003,7 +1003,33 @@ Some other text (org-element-property :FOO (org-element-at-point)))) (should-not (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