浏览代码

org-element: Fix top-level property-drawer parsing

* lisp/org-element.el (org-element-comment-parser): Top level comments
do not have affiliated keywords.
(org-element--current-element): Properly parse top-level comments and
top-level properties drawer.
(org-element--next-mode): Change signature.
(org-element--parse-elements): Apply signature change.
(org-element--parse-to): Apply signature change.  Look for top-level
comments or properties drawer.
* testing/lisp/test-org-element.el (test-org-element/property-drawer-parser):
Add comment.

Initially, `org-element--current-element' would defer to
`org-get-property-block' to validate a property-drawer, which is the
wrong way to look at the low-level Org Element library. Also, it would
allow affiliated keywords, which is not possible.
Nicolas Goaziou 5 年之前
父节点
当前提交
af99544286
共有 2 个文件被更改,包括 45 次插入35 次删除
  1. 43 35
      lisp/org-element.el
  2. 2 0
      testing/lisp/test-org-element.el

+ 43 - 35
lisp/org-element.el

@@ -1820,7 +1820,7 @@ containing `:begin', `:end', `:value', `:post-blank',
 
 Assume point is at comment beginning."
   (save-excursion
-    (let* ((begin (car affiliated))
+    (let* ((begin (or (car affiliated) (point)))
 	   (post-affiliated (point))
 	   (value (prog2 (looking-at "[ \t]*# ?")
 		      (buffer-substring-no-properties
@@ -3823,12 +3823,6 @@ Assume point is at the first equal sign marker."
 ;; `org-element--current-element' is the core function of this section.
 ;; It returns the Lisp representation of the element starting at
 ;; point.
-;;
-;; `org-element--current-element' makes use of special modes.  They
-;; are activated for fixed element chaining (e.g., `plain-list' >
-;; `item') or fixed conditional element chaining (e.g., `headline' >
-;; `section').  Special modes are: `first-section', `item',
-;; `node-property', `section' and `table-row'.
 
 (defun org-element--current-element (limit &optional granularity mode structure)
   "Parse the element starting at point.
@@ -3848,8 +3842,9 @@ nil), secondary values will not be parsed, since they only
 contain objects.
 
 Optional argument MODE, when non-nil, can be either
-`first-section', `section', `planning', `item', `node-property'
-and `table-row'.
+`first-section', `item', `node-property', `planning',
+`property-drawer', `section', `table-row', or `top-comment'.
+
 
 If STRUCTURE isn't provided but MODE is set to `item', it will be
 computed.
@@ -3879,15 +3874,22 @@ element it has to parse."
 	(org-element-section-parser
 	 (or (save-excursion (org-with-limited-levels (outline-next-heading)))
 	     limit)))
+       ;; Top-level comments.  Those cannot have affiliated keywords.
+       ((and (eq mode 'top-comment) (looking-at "#\\(?: \\|$\\)"))
+	(org-element-comment-parser limit nil))
        ;; Planning.
        ((and (eq mode 'planning)
 	     (eq ?* (char-after (line-beginning-position 0)))
 	     (looking-at org-planning-line-re))
 	(org-element-planning-parser limit))
        ;; Property drawer.
-       ((and (memq mode '(planning property-drawer))
-	     (eq ?* (char-after (line-beginning-position
-				 (if (eq mode 'planning) 0 -1))))
+       ((and (pcase mode
+	       (`planning (eq ?* (char-after (line-beginning-position 0))))
+	       ((or `property-drawer `top-comment)
+		(save-excursion
+		  (beginning-of-line 0)
+		  (not (looking-at "[[:blank:]]*$"))))
+	       (_ nil))
 	     (looking-at org-property-drawer-re))
 	(org-element-property-drawer-parser limit))
        ;; When not at bol, point is at the beginning of an item or
@@ -3910,9 +3912,6 @@ element it has to parse."
 	     ;; LaTeX Environment.
 	     ((looking-at org-element--latex-begin-environment)
 	      (org-element-latex-environment-parser limit affiliated))
-	     ;; Property drawer (before first headline, else it's catched above).
-	     ((org-at-property-block-p)
-	      (org-element-property-drawer-parser limit))
 	     ;; Drawer.
 	     ((looking-at org-drawer-regexp)
 	      (org-element-drawer-parser limit affiliated))
@@ -4323,34 +4322,41 @@ looking into captions:
 ;; `org-element--object-lex' to find the next object in the current
 ;; container.
 
-(defsubst org-element--next-mode (type parentp)
-  "Return next special mode according to TYPE, or nil.
-TYPE is a symbol representing the type of an element or object
-containing next element if PARENTP is non-nil, or before it
-otherwise.  Modes can be either `first-section', `item',
-`node-property', `planning', `property-drawer', `section',
-`table-row' or nil."
-  (if parentp
+(defsubst org-element--next-mode (mode type parent?)
+  "Return next mode according to current one.
+
+MODE is a symbol representing the expectation about the next
+element or object.  Meaningful values are `first-section',
+`item', `node-property', `planning', `property-drawer',
+`section', `table-row', `top-comment', and nil.
+
+TYPE is the type of the current element or object.
+
+If PARENT? is non-nil, assume the next element or object will be
+located inside the current one.  "
+  (if parent?
       (pcase type
 	(`headline 'section)
+	(`first-section 'top-comment)
 	(`inlinetask 'planning)
 	(`plain-list 'item)
 	(`property-drawer 'node-property)
 	(`section 'planning)
 	(`table 'table-row))
-    (pcase type
+    (pcase mode
       (`item 'item)
       (`node-property 'node-property)
-      (`planning 'property-drawer)
-      (`table-row 'table-row))))
+      ((and `planning (guard (eq type 'planning))) 'property-drawer)
+      (`table-row 'table-row)
+      ((and `top-comment (guard (eq type 'comment))) 'property-drawer))))
 
 (defun org-element--parse-elements
     (beg end mode structure granularity visible-only acc)
   "Parse elements between BEG and END positions.
 
 MODE prioritizes some elements over the others.  It can be set to
-`first-section', `section', `planning', `item', `node-property'
-or `table-row'.
+`first-section', `item', `node-property', `planning',
+`property-drawer', `section', `table-row', `top-comment', or nil.
 
 When value is `item', STRUCTURE will be used as the current list
 structure.
@@ -4399,7 +4405,7 @@ Elements are accumulated into ACC."
 	    (org-element--parse-elements
 	     cbeg (org-element-property :contents-end element)
 	     ;; Possibly switch to a special mode.
-	     (org-element--next-mode type t)
+	     (org-element--next-mode mode type t)
 	     (and (memq type '(item plain-list))
 		  (org-element-property :structure element))
 	     granularity visible-only element))
@@ -4411,7 +4417,7 @@ Elements are accumulated into ACC."
 	     (org-element-restriction type))))
 	  (push (org-element-put-property element :parent acc) elements)
 	  ;; Update mode.
-	  (setq mode (org-element--next-mode type nil))))
+	  (setq mode (org-element--next-mode mode type nil))))
       ;; Return result.
       (apply #'org-element-set-contents acc (nreverse elements)))))
 
@@ -5453,9 +5459,11 @@ the process stopped before finding the expected result."
         ;; element following headline above, or first element in
         ;; buffer.
         ((not cached)
-         (when (org-with-limited-levels (outline-previous-heading))
-           (setq mode 'planning)
-	   (forward-line))
+         (if (org-with-limited-levels (outline-previous-heading))
+             (progn
+	       (setq mode 'planning)
+	       (forward-line))
+	   (setq mode 'top-comment))
          (skip-chars-forward " \r\t\n")
          (beginning-of-line))
         ;; Cache returned exact match: return it.
@@ -5524,7 +5532,7 @@ the process stopped before finding the expected result."
 	      ;; after it.
 	      ((and (<= elem-end pos) (/= (point-max) elem-end))
 	       (goto-char elem-end)
-	       (setq mode (org-element--next-mode type nil)))
+	       (setq mode (org-element--next-mode mode type nil)))
 	      ;; A non-greater element contains point: return it.
 	      ((not (memq type org-element-greater-elements))
 	       (throw 'exit element))
@@ -5552,7 +5560,7 @@ the process stopped before finding the expected result."
 				    (and (= cend pos) (= (point-max) pos)))))
 		   (goto-char (or next cbeg))
 		   (setq next nil
-			 mode (org-element--next-mode type t)
+			 mode (org-element--next-mode mode type t)
 			 parent element
 			 end cend))))
 	      ;; Otherwise, return ELEMENT as it is the smallest

+ 2 - 0
testing/lisp/test-org-element.el

@@ -2120,6 +2120,8 @@ Outside list"
        (org-test-with-temp-text
 	   "* H\nDEADLINE: <2014-03-04 tue.>\n<point>:PROPERTIES:\n:prop: value\n:END:"
 	 (org-element-type (org-element-at-point)))))
+  ;; Parse property drawer at the beginning of the document, possibly
+  ;; after some initial comments.
   (should
    (eq 'property-drawer
        (org-test-with-temp-text "<point>:PROPERTIES:\n:prop: value\n:END:"