forked from mirrors/org-mode
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
This commit is contained in:
parent
2e5b3dede1
commit
37bf0576f2
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue