summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2014-08-31 11:10:56 +0200
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2014-08-31 11:10:56 +0200
commit37bf0576f2f2894c6e37239ee8db63a3ef21a840 (patch)
tree35bc65aa97768cf1281f19de85d38275191f1c9f
parent2e5b3dede103bba0071144ec7b7fc250471c1463 (diff)
downloadorg-mode-37bf0576f2f2894c6e37239ee8db63a3ef21a840.tar.gz
org-element: Make properties parsing more robust
* lisp/org-element.el (org-element-property-drawer-parser, org-element-node-property-parser): Ignore lines that are not node properties. (org-element-node-property-interpreter): Allow nil properties. * lisp/org.el (org-re-property): Fix regexp to match properties with empty values. * testing/lisp/test-org-element.el (test-org-element/node-property): Add tests. Thanks to Eike for reporting it. http://permalink.gmane.org/gmane.emacs.orgmode/90293
-rw-r--r--lisp/org-element.el92
-rw-r--r--lisp/org.el6
-rw-r--r--testing/lisp/test-org-element.el34
3 files changed, 73 insertions, 59 deletions
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 76c93ce..eb8ff41 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -1312,36 +1312,36 @@ containing `:begin', `:end', `:hiddenp', `:contents-begin',
`:contents-end', `:post-blank' and `:post-affiliated' keywords.
Assume point is at the beginning of the property drawer."
- (save-excursion
- (let ((case-fold-search t))
- (if (not (save-excursion
- (re-search-forward "^[ \t]*:END:[ \t]*$" limit t)))
- ;; Incomplete drawer: parse it as a paragraph.
- (org-element-paragraph-parser limit affiliated)
- (save-excursion
- (let* ((drawer-end-line (match-beginning 0))
- (begin (car affiliated))
- (post-affiliated (point))
- (contents-begin (progn (forward-line)
- (and (< (point) drawer-end-line)
- (point))))
- (contents-end (and contents-begin drawer-end-line))
- (hidden (org-invisible-p2))
- (pos-before-blank (progn (goto-char drawer-end-line)
- (forward-line)
- (point)))
- (end (progn (skip-chars-forward " \r\t\n" limit)
- (if (eobp) (point) (line-beginning-position)))))
- (list 'property-drawer
- (nconc
- (list :begin begin
- :end end
- :hiddenp hidden
- :contents-begin contents-begin
- :contents-end contents-end
- :post-blank (count-lines pos-before-blank end)
- :post-affiliated post-affiliated)
- (cdr affiliated)))))))))
+ (let ((case-fold-search t))
+ (if (not (save-excursion (re-search-forward "^[ \t]*:END:[ \t]*$" limit t)))
+ ;; Incomplete drawer: parse it as a paragraph.
+ (org-element-paragraph-parser limit affiliated)
+ (save-excursion
+ (let* ((drawer-end-line (match-beginning 0))
+ (begin (car affiliated))
+ (post-affiliated (point))
+ (contents-begin
+ (progn
+ (forward-line)
+ (and (re-search-forward org-property-re drawer-end-line t)
+ (line-beginning-position))))
+ (contents-end (and contents-begin drawer-end-line))
+ (hidden (org-invisible-p2))
+ (pos-before-blank (progn (goto-char drawer-end-line)
+ (forward-line)
+ (point)))
+ (end (progn (skip-chars-forward " \r\t\n" limit)
+ (if (eobp) (point) (line-beginning-position)))))
+ (list 'property-drawer
+ (nconc
+ (list :begin begin
+ :end end
+ :hiddenp hidden
+ :contents-begin contents-begin
+ :contents-end contents-end
+ :post-blank (count-lines pos-before-blank end)
+ :post-affiliated post-affiliated)
+ (cdr affiliated))))))))
(defun org-element-property-drawer-interpreter (property-drawer contents)
"Interpret PROPERTY-DRAWER element as Org syntax.
@@ -2096,28 +2096,28 @@ LIMIT bounds the search.
Return a list whose CAR is `node-property' and CDR is a plist
containing `:key', `:value', `:begin', `:end' and `:post-blank'
keywords."
- (save-excursion
- (looking-at org-property-re)
- (let ((case-fold-search t)
- (begin (point))
- (key (org-match-string-no-properties 2))
- (value (org-match-string-no-properties 3))
- (pos-before-blank (progn (forward-line) (point)))
- (end (progn (skip-chars-forward " \r\t\n" limit)
- (if (eobp) (point) (point-at-bol)))))
- (list 'node-property
- (list :key key
- :value value
- :begin begin
- :end end
- :post-blank (count-lines pos-before-blank end))))))
+ (looking-at org-property-re)
+ (let ((begin (point))
+ (key (org-match-string-no-properties 2))
+ (value (org-match-string-no-properties 3))
+ (end (save-excursion
+ (end-of-line)
+ (if (re-search-forward org-property-re limit t)
+ (line-beginning-position)
+ limit))))
+ (list 'node-property
+ (list :key key
+ :value value
+ :begin begin
+ :end end
+ :post-blank 0))))
(defun org-element-node-property-interpreter (node-property contents)
"Interpret NODE-PROPERTY element as Org syntax.
CONTENTS is nil."
(format org-property-format
(format ":%s:" (org-element-property :key node-property))
- (org-element-property :value node-property)))
+ (or (org-element-property :value node-property) "")))
;;;; Paragraph
diff --git a/lisp/org.el b/lisp/org.el
index 60658f4..af450cc 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -6156,9 +6156,9 @@ Use `org-reduced-level' to remove the effect of `org-odd-levels'."
Match group 3 will be set to the value if it exists."
(concat "^\\(?4:[ \t]*\\)\\(?1::\\(?2:"
(if literal property (regexp-quote property))
- "\\):\\)[ \t]+\\(?3:[^ \t\r\n]"
- (if allow-null "*")
- ".*?\\)\\(?5:[ \t]*\\)$"))
+ "\\):\\)\\(?:[ \t]+\\(?3:[^ \t\r\n].*?\\)\\)"
+ (and allow-null "?")
+ "\\(?5:[ \t]*\\)$"))
(defconst org-property-re
(org-re-property ".*?" 'literal t)
diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el
index 12cd2bd..fdd654f 100644
--- a/testing/lisp/test-org-element.el
+++ b/testing/lisp/test-org-element.el
@@ -1479,23 +1479,37 @@ e^{i\\pi}+1=0
;; Standard test.
(should
(equal '("abc" "value")
- (org-test-with-temp-text ":PROPERTIES:\n:abc: value\n:END:"
- (progn (forward-line)
- (let ((element (org-element-at-point)))
- (list (org-element-property :key element)
- (org-element-property :value element)))))))
+ (org-test-with-temp-text ":PROPERTIES:\n<point>:abc: value\n:END:"
+ (let ((element (org-element-at-point)))
+ (list (org-element-property :key element)
+ (org-element-property :value element))))))
;; Value should be trimmed.
(should
(equal "value"
- (org-test-with-temp-text ":PROPERTIES:\n:abc: value \n:END:"
- (progn (forward-line)
- (let ((element (org-element-at-point)))
- (org-element-property :value element))))))
+ (org-test-with-temp-text ":PROPERTIES:\n<point>:abc: value \n:END:"
+ (org-element-property :value (org-element-at-point)))))
;; A node property requires to be wrapped within a property drawer.
(should-not
(eq 'node-property
(org-test-with-temp-text ":abc: value"
- (org-element-type (org-element-at-point))))))
+ (org-element-type (org-element-at-point)))))
+ ;; Accept empty properties.
+ (should
+ (equal '(("foo" "value") ("bar" nil))
+ (org-test-with-temp-text ":PROPERTIES:\n:foo: value\n:bar:\n:END:"
+ (org-element-map (org-element-parse-buffer) 'node-property
+ (lambda (p)
+ (list (org-element-property :key p)
+ (org-element-property :value p)))))))
+ ;; Ignore all non-property lines in property drawers.
+ (should
+ (equal
+ '(("foo" "value"))
+ (org-test-with-temp-text ":PROPERTIES:\nWrong1\n:foo: value\nWrong2\n:END:"
+ (org-element-map (org-element-parse-buffer) 'node-property
+ (lambda (p)
+ (list (org-element-property :key p)
+ (org-element-property :value p))))))))
;;;; Paragraph