Sfoglia il codice sorgente

org-element: Refactor code, add tests

* contrib/lisp/org-element.el (org-element-babel-call-parser): Be sure
  to match "#+CALL:".
(org-element-block-name-alist): New variable.
(org-element-non-recursive-block-alist): Removed variable.
(org-element-current-element): Refactor.
* testing/lisp/test-org-element.el: Add tests.
Nicolas Goaziou 13 anni fa
parent
commit
d83ab52626
2 ha cambiato i file con 651 aggiunte e 75 eliminazioni
  1. 46 61
      contrib/lisp/org-element.el
  2. 605 14
      testing/lisp/test-org-element.el

+ 46 - 61
contrib/lisp/org-element.el

@@ -46,9 +46,8 @@
 ;; `comment-block', `example-block', `export-block', `fixed-width',
 ;; `horizontal-rule', `keyword', `latex-environment', `paragraph',
 ;; `planning', `property-drawer', `quote-section', `src-block',
-;; `table', `table-cell', `table-row' and `verse-block'.  Among them,
-;; `paragraph', `table-cell' and `verse-block' types can contain Org
-;; objects and plain text.
+;; `table', `table-row' and `verse-block'.  Among them, `paragraph'
+;; and `verse-block' types can contain Org objects and plain text.
 ;;
 ;; Objects are related to document's contents.  Some of them are
 ;; recursive.  Associated types are of the following: `bold', `code',
@@ -892,7 +891,8 @@ Return a list whose CAR is `babel-call' and CDR is a plist
 containing `:begin', `:end', `:info' and `:post-blank' as
 keywords."
   (save-excursion
-    (let ((info (progn (looking-at org-babel-block-lob-one-liner-regexp)
+    (let ((case-fold-search t)
+	  (info (progn (looking-at org-babel-block-lob-one-liner-regexp)
 		       (org-babel-lob-get-info)))
 	  (begin (point-at-bol))
 	  (pos-before-blank (progn (forward-line) (point)))
@@ -2851,17 +2851,21 @@ regexp matching one object can also match the other object.")
 	 table-cell underline)
   "List of recursive object types.")
 
-(defconst org-element-non-recursive-block-alist
+(defconst org-element-block-name-alist
   '(("ASCII" . export-block)
+    ("CENTER" . center-block)
     ("COMMENT" . comment-block)
     ("DOCBOOK" . export-block)
     ("EXAMPLE" . example-block)
     ("HTML" . export-block)
     ("LATEX" . export-block)
     ("ODT" . export-block)
+    ("QUOTE" . quote-block)
     ("SRC" . src-block)
     ("VERSE" . verse-block))
-  "Alist between non-recursive block name and their element type.")
+  "Alist between block names and their element type.
+Any block whose name has no association in the current list has
+a `special-block' type.")
 
 (defconst org-element-affiliated-keywords
   '("ATTR_ASCII" "ATTR_DOCBOOK" "ATTR_HTML" "ATTR_LATEX" "ATTR_ODT" "CAPTION"
@@ -3044,9 +3048,8 @@ Optional argument SPECIAL, when non-nil, can be either `section',
 If STRUCTURE isn't provided but SPECIAL is set to `item', it will
 be computed.
 
-Unlike to `org-element-at-point', this function assumes point is
-always at the beginning of the element it has to parse.  As such,
-it is quicker than its counterpart, albeit more restrictive."
+This function assumes point is always at the beginning of the
+element it has to parse."
   (save-excursion
     ;; If point is at an affiliated keyword, try moving to the
     ;; beginning of the associated element.  If none is found, the
@@ -3061,7 +3064,7 @@ it is quicker than its counterpart, albeit more restrictive."
 	  ;; `org-element-secondary-value-alist'.
 	  (raw-secondary-p (and granularity (not (eq granularity 'object)))))
       (cond
-       ;; Item
+       ;; Item.
        ((eq special 'item)
 	(org-element-item-parser (or structure (org-list-struct))
 				 raw-secondary-p))
@@ -3079,67 +3082,49 @@ it is quicker than its counterpart, albeit more restrictive."
 	(if (equal (match-string 1) org-clock-string)
 	    (org-element-clock-parser)
 	  (org-element-planning-parser)))
-       ;; Non-recursive block.
-       ((when (looking-at org-element--element-block-re)
-          (let ((type (upcase (match-string 1))))
-            (if (save-excursion
-                  (re-search-forward
-                   (format "^[ \t]*#\\+END_%s\\(?: \\|$\\)" type) nil t))
-                (funcall
-		 (intern
-		  (format
-		   "org-element-%s-parser"
-		   (cdr (assoc type org-element-non-recursive-block-alist)))))
-              (org-element-paragraph-parser)))))
+       ;; Blocks.
+       ((when (looking-at "[ \t]*#\\+BEGIN_\\([-A-Za-z0-9]+\\)\\(?: \\|$\\)")
+          (let ((name (upcase (match-string 1))) type)
+            (cond
+	     ((not (save-excursion
+		     (re-search-forward
+		      (format "^[ \t]*#\\+END_%s\\(?: \\|$\\)" name) nil t)))
+	      (org-element-paragraph-parser))
+	     ((setq type (assoc name org-element-block-name-alist))
+	      (funcall (intern (format "org-element-%s-parser" (cdr type)))))
+	     (t (org-element-special-block-parser))))))
        ;; Inlinetask.
        ((org-at-heading-p) (org-element-inlinetask-parser raw-secondary-p))
-       ;; LaTeX Environment or Paragraph if incomplete.
+       ;; LaTeX Environment.
        ((looking-at "[ \t]*\\\\begin{")
         (if (save-excursion
               (re-search-forward "[ \t]*\\\\end{[^}]*}[ \t]*" nil t))
             (org-element-latex-environment-parser)
           (org-element-paragraph-parser)))
-       ;; Property Drawer.
-       ((looking-at org-property-start-re)
-        (if (save-excursion (re-search-forward org-property-end-re nil t))
-            (org-element-property-drawer-parser)
-          (org-element-paragraph-parser)))
-       ;; Recursive Block, or Paragraph if incomplete.
-       ((looking-at "[ \t]*#\\+BEGIN_\\([-A-Za-z0-9]+\\)\\(?: \\|$\\)")
-        (let ((type (upcase (match-string 1))))
-          (cond
-           ((not (save-excursion
-                   (re-search-forward
-                    (format "^[ \t]*#\\+END_%s\\(?: \\|$\\)" type) nil t)))
-            (org-element-paragraph-parser))
-           ((string= type "CENTER") (org-element-center-block-parser))
-           ((string= type "QUOTE") (org-element-quote-block-parser))
-           (t (org-element-special-block-parser)))))
-       ;; Drawer.
+       ;; Drawer and Property Drawer.
        ((looking-at org-drawer-regexp)
-        (if (save-excursion (re-search-forward "^[ \t]*:END:[ \t]*$" nil t))
-            (org-element-drawer-parser)
-          (org-element-paragraph-parser)))
+        (let ((name (match-string 1)))
+	  (cond
+	   ((not (save-excursion (re-search-forward "^[ \t]*:END:[ \t]*$" nil t)))
+	    (org-element-paragraph-parser))
+	   ((equal "PROPERTIES" name) (org-element-property-drawer-parser))
+	   (t (org-element-drawer-parser)))))
+       ;; Fixed Width
        ((looking-at "[ \t]*:\\( \\|$\\)") (org-element-fixed-width-parser))
-       ;; Babel Call.
-       ((looking-at org-babel-block-lob-one-liner-regexp)
-        (org-element-babel-call-parser))
-       ;; Dynamic Block or Paragraph if incomplete.  This must be
-       ;; checked before regular keywords since their regexp matches
-       ;; dynamic blocks too.
-       ((looking-at "[ \t]*#\\+BEGIN:\\(?: \\|$\\)")
-        (if (save-excursion
-	      (re-search-forward "^[ \t]*#\\+END:\\(?: \\|$\\)" nil t))
-            (org-element-dynamic-block-parser)
-          (org-element-paragraph-parser)))
-       ;; Keyword, or Paragraph if at an orphaned affiliated keyword.
+       ;; Babel Call, Dynamic Block and Keyword.
        ((looking-at "[ \t]*#\\+\\([a-z]+\\(:?_[a-z]+\\)*\\):")
         (let ((key (upcase (match-string 1))))
-          (if (or (string= key "TBLFM")
-                  (member key org-element-affiliated-keywords))
-              (org-element-paragraph-parser)
-            (org-element-keyword-parser))))
-       ;; Footnote definition.
+          (cond
+	   ((equal key "CALL") (org-element-babel-call-parser))
+	   ((and (equal key "BEGIN")
+		 (save-excursion
+		   (re-search-forward "^[ \t]*#\\+END:\\(?: \\|$\\)" nil t)))
+	    (org-element-dynamic-block-parser))
+	   ((and (not (equal key "TBLFM"))
+		 (not (member key org-element-affiliated-keywords)))
+	    (org-element-keyword-parser))
+	   (t (org-element-paragraph-parser)))))
+       ;; Footnote Definition.
        ((looking-at org-footnote-definition-re)
         (org-element-footnote-definition-parser))
        ;; Comment.
@@ -3150,7 +3135,7 @@ it is quicker than its counterpart, albeit more restrictive."
         (org-element-horizontal-rule-parser))
        ;; Table.
        ((org-at-table-p t) (org-element-table-parser))
-       ;; List or Item.
+       ;; List.
        ((looking-at (org-item-re))
         (org-element-plain-list-parser (or structure (org-list-struct))))
        ;; Default element: Paragraph.

+ 605 - 14
testing/lisp/test-org-element.el

@@ -36,10 +36,118 @@ Return interpreted string."
 
 ;;; Test Parsers
 
-;;;; Comments
+;;;; Babel Call
+
+(ert-deftest test-org-element/babel-call-parser ()
+  "Test `babel-call' parsing."
+  (should
+   (equal
+    (org-test-with-temp-text "#+CALL: test()"
+      (org-element-map (org-element-parse-buffer) 'babel-call 'identity nil t))
+    '(babel-call (:begin 1 :end 15 :info ("test()" nil 0) :post-blank 0)))))
+
+
+;;;; Bold
+
+(ert-deftest test-org-element/bold-parser ()
+  "Test `bold' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (org-test-with-temp-text "*bold*"
+      (org-element-map (org-element-parse-buffer) 'bold 'identity nil t))
+    '(bold (:begin 1 :end 7 :contents-begin 2 :contents-end 6 :post-blank 0)
+	   "bold")))
+  ;; Multi-line markup.
+  (should
+   (equal
+    (org-test-with-temp-text "*first line\nsecond line*"
+      (org-element-map (org-element-parse-buffer) 'bold 'identity nil t))
+    '(bold (:begin 1 :end 25 :contents-begin 2 :contents-end 24 :post-blank 0)
+	   "first line\nsecond line"))))
+
+
+;;;; Center Block
+
+(ert-deftest test-org-element/center-block-parser ()
+  "Test `center-block' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_CENTER\nText\n#+END_CENTER"
+      (org-element-map
+       (org-element-parse-buffer) 'center-block 'identity nil t))
+    '(center-block
+      (:begin 1 :end 33 :hiddenp nil :contents-begin 16 :contents-end 21
+	      :post-blank 0)
+      (paragraph
+       (:begin 16 :end 21 :contents-begin 16 :contents-end 20 :post-blank 0)
+       "Text"))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_CENTER\nText\n#+END_CENTER"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'center-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_CENTER"
+     (org-element-map
+      (org-element-parse-buffer) 'center-block 'identity nil t))))
+
+
+;;;; Clock
+
+(ert-deftest test-org-element/clock-parser ()
+  "Test `clock' parser."
+  ;; Running clock.
+  (should
+   (equal
+    (let ((org-clock-string "CLOCK:"))
+      (org-test-with-temp-text "CLOCK: [2012-01-01 sun. 00:01]"
+	(org-element-map
+	 (org-element-parse-buffer) 'clock 'identity nil t)))
+    '(clock
+      (:status running :value "[2012-01-01 sun. 00:01]" :time nil :begin 1
+	       :end 31 :post-blank 0))))
+  ;; Closed clock.
+  (should
+   (equal
+    (let ((org-clock-string "CLOCK:"))
+      (org-test-with-temp-text "
+CLOCK: [2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02] =>  0:01"
+	(org-element-map
+	 (org-element-parse-buffer) 'clock 'identity nil t)))
+    '(clock
+      (:status closed
+	       :value "[2012-01-01 sun. 00:01]--[2012-01-01 sun. 00:02]"
+	       :time "0:01" :begin 2 :end 66 :post-blank 0)))))
+
+
+;;;; Code
+
+(ert-deftest test-org-element/code-parser ()
+  "Test `code' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (org-test-with-temp-text "~code~"
+      (org-element-map (org-element-parse-buffer) 'code 'identity nil t))
+    '(code (:value "code" :begin 1 :end 7 :post-blank 0))))
+  ;; Multi-line markup.
+  (should
+   (equal
+    (org-test-with-temp-text "~first line\nsecond line~"
+      (org-element-map (org-element-parse-buffer) 'code 'identity nil t))
+    '(code (:value "first line\nsecond line" :begin 1 :end 25 :post-blank 0)))))
+
+
+;;;; Comment
 
 (ert-deftest test-org-element/comment-parser ()
-  "Test `comment' parsing."
+  "Test `comment' parser."
   ;; Regular comment.
   (should
    (equal
@@ -64,10 +172,159 @@ Return interpreted string."
     (org-test-with-temp-text "#+ First part\n#+ \n#+\n#+ Second part"
       (org-element-map (org-element-parse-buffer) 'comment 'identity nil t))
     '(comment
-      (:begin 1 :end 36 :value "First part\n\n\nSecond part\n" :post-blank 0)))))
+      (:begin 1 :end 36 :value "First part\n\n\nSecond part\n"
+	      :post-blank 0)))))
+
 
+;;;; Comment Block
+
+(ert-deftest test-org-element/comment-block-parser ()
+  "Test `comment-block' parser."
+  ;; Regular tests.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_COMMENT\nText\n#+END_COMMENT"
+      (org-element-map
+       (org-element-parse-buffer) 'comment-block 'identity nil t))
+    '(comment-block (:begin 1 :end 35 :value "Text\n" :hiddenp nil
+			    :post-blank 0))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_COMMENT\nText\n#+END_COMMENT"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'comment-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_COMMENT"
+     (org-element-map
+      (org-element-parse-buffer) 'comment-block 'identity nil t))))
 
-;;;; Example-blocks and Src-blocks
+
+;;;; Drawer
+
+(ert-deftest test-org-element/drawer-parser ()
+  "Test `drawer' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (let ((org-drawers '("TEST")))
+      (org-test-with-temp-text ":TEST:\nText\n:END:"
+	(org-element-map (org-element-parse-buffer) 'drawer 'identity nil t)))
+    '(drawer
+      (:begin 1 :end 18 :drawer-name "TEST" :hiddenp nil :contents-begin 8
+	      :contents-end 13 :post-blank 0)
+      (paragraph
+       (:begin 8 :end 13 :contents-begin 8 :contents-end 12 :post-blank 0)
+       "Text"))))
+  ;; Do not mix regular drawers and property drawers.
+  (should-not
+   (let ((org-drawers '("PROPERTIES")))
+     (org-test-with-temp-text ":PROPERTIES:\n:prop: value\n:END:"
+       (org-element-map
+	(org-element-parse-buffer) 'drawer 'identity nil t))))
+  ;; Ignore incomplete drawer.
+  (should-not
+   (let ((org-drawers '("TEST")))
+     (org-test-with-temp-text ":TEST:"
+       (org-element-map
+	(org-element-parse-buffer) 'drawer 'identity nil t)))))
+
+
+;;;; Dynamic Block
+
+(ert-deftest test-org-element/dynamic-block-parser ()
+  "Test `dynamic-block' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (org-test-with-temp-text
+	"#+BEGIN: myblock :param1 val1 :param2 val2\nText\n#+END:"
+      (org-element-map
+       (org-element-parse-buffer) 'dynamic-block 'identity nil t))
+    '(dynamic-block
+      (:begin 1 :end 55 :block-name "myblock"
+	      :arguments ":param1 val1 :param2 val2" :hiddenp nil
+	      :contents-begin 44 :contents-end 49 :post-blank 0)
+      (paragraph
+       (:begin 44 :end 49 :contents-begin 44 :contents-end 48 :post-blank 0)
+       "Text"))))
+  ;; Folded view
+  (org-test-with-temp-text
+      "#+BEGIN: myblock :param1 val1 :param2 val2\nText\n#+END:"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'dynamic-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN: myblock :param1 val1 :param2 val2"
+     (org-element-map
+      (org-element-parse-buffer) 'dynamic-block 'identity nil t))))
+
+
+;;;; Entity
+
+(ert-deftest test-org-element/entity-parser ()
+  "Test `entity' parser."
+  ;; Without brackets.
+  (should
+   (equal
+    (org-test-with-temp-text "\\sin"
+      (org-element-map (org-element-parse-buffer) 'entity 'identity nil t))
+    '(entity
+      (:name "sin" :latex "\\sin" :latex-math-p t :html "sin"
+	     :ascii "sin" :latin1 "sin" :utf-8 "sin" :begin 1 :end 5
+	     :use-brackets-p nil :post-blank 0))))
+  ;; With brackets.
+  (should
+   (org-element-property
+    :use-brackets-p
+    (org-test-with-temp-text "\\alpha{}text"
+      (org-element-map (org-element-parse-buffer) 'entity 'identity nil t))))
+  ;; User-defined entity.
+  (should
+   (equal
+    (org-element-property
+     :name
+     (let ((org-entities-user
+	    '(("test" "test" nil "test" "test" "test" "test"))))
+       (org-test-with-temp-text "\\test"
+	 (org-element-map (org-element-parse-buffer) 'entity 'identity nil t))))
+    "test")))
+
+
+;;;; Example Block
+
+(ert-deftest test-org-element/example-block-parser ()
+  "Test `example-block' parser."
+  ;; Regular tests.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText\n#+END_EXAMPLE"
+      (org-element-map
+       (org-element-parse-buffer) 'example-block 'identity nil t))
+    '(example-block
+      (:begin 1 :end 35 :value "Text\n" :switches nil
+	      :number-lines nil :preserve-indent nil :retain-labels t
+	      :use-labels t :label-fmt nil :hiddenp nil :post-blank 0))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_EXAMPLE\nText\n#+END_EXAMPLE"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'example-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_EXAMPLE"
+     (org-element-map
+      (org-element-parse-buffer) 'example-block 'identity nil t))))
 
 (ert-deftest test-org-element/block-switches ()
   "Test `example-block' and `src-block' switches parsing."
@@ -169,10 +426,38 @@ Return interpreted string."
 	 (equal (org-element-property :label-fmt element) "[ref:%s]"))))))
 
 
-;;;; Export snippets
+;;;; Export Block
+
+(ert-deftest test-org-element/export-block-parser ()
+  "Test `export-block' parser."
+  ;; Regular tests.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_LATEX\nText\n#+END_LATEX"
+      (org-element-map
+       (org-element-parse-buffer) 'export-block 'identity nil t))
+    '(export-block
+      (:begin 1 :end 31 :type "LATEX" :value "Text\n" :hiddenp nil
+	      :post-blank 0))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_LATEX\nText\n#+END_LATEX"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'export-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_LATEX"
+     (org-element-map
+      (org-element-parse-buffer) 'export-block 'identity nil t))))
+
+
+;;;; Export Snippet
 
 (ert-deftest test-org-element/export-snippet-parser ()
-  "Test export-snippet parsing."
+  "Test `export-snippet' parser."
   (should
    (equal
     (org-test-with-temp-text "<back-end@contents>"
@@ -183,7 +468,7 @@ Return interpreted string."
 		 :value "contents" :begin 1 :end 20 :post-blank 0)))))
 
 
-;;;; Fixed width
+;;;; Fixed Width
 
 (ert-deftest test-org-element/fixed-width ()
   "Test fixed-width area parsing."
@@ -220,10 +505,27 @@ Return interpreted string."
 	  (org-element-parse-buffer) 'fixed-width 'identity))))))
 
 
-;;;; Footnotes references
+;;;; Footnote Definition.
+
+(ert-deftest test-org-element/footnote-definition-parser ()
+  "Test `footnote-definition' parser."
+  (should
+   (equal
+    (org-test-with-temp-text "[fn:1] Definition"
+      (org-element-map
+       (org-element-parse-buffer) 'footnote-definition 'identity nil t))
+    '(footnote-definition
+      (:label "fn:1" :begin 1 :end 18 :contents-begin 8 :contents-end 18
+	      :post-blank 0)
+      (paragraph
+       (:begin 8 :end 18 :contents-begin 8 :contents-end 18 :post-blank 0)
+       "Definition")))))
+
+
+;;;; Footnotes Reference
 
 (ert-deftest test-org-element/footnote-reference-parser ()
-  "Test footnote-reference parsing."
+  "Test `footnote-reference' parser."
   ;; 1. Parse a standard reference.
   (org-test-with-temp-text "[fn:label]"
     (should (equal (org-element-footnote-reference-parser)
@@ -346,10 +648,142 @@ Return interpreted string."
 	(should (equal (org-element-property :tags headline) '("test")))))))
 
 
-;;;; Links
+;;;; Inlinetask.
+
+(ert-deftest test-org-element/inlinetask-parser ()
+  "Test `inlinetask' parser."
+  (when (featurep 'org-inlinetask)
+    (let ((org-inlinetask-min-level 15))
+      ;; 1. Regular inlinetask.
+      (should
+       (equal
+	(org-test-with-temp-text
+	    "*************** Task\nTest\n*************** END"
+	  (org-element-map
+	   (org-element-parse-buffer) 'inlinetask 'identity nil t))
+	'(inlinetask
+	  (:title ("Task") :begin 1 :end 46 :hiddenp nil :contents-begin 22
+		  :contents-end 27 :level 15 :priority nil :tags nil
+		  :todo-keyword nil :todo-type nil :scheduled nil :deadline nil
+		  :timestamp nil :clock nil :post-blank 0 :category "???")
+	  (paragraph
+	   (:begin 22 :end 27 :contents-begin 22 :contents-end 26 :post-blank 0)
+	   "Test"))))
+      ;; 2. Degenerate inlinetask.
+      (should
+       (equal
+	(org-test-with-temp-text
+	    "*************** Task"
+	  (org-element-map
+	   (org-element-parse-buffer) 'inlinetask 'identity nil t))
+	'(inlinetask
+	  (:title ("Task") :begin 1 :end 21 :hiddenp nil :contents-begin 21
+		  :contents-end 21 :level 15 :priority nil :tags nil
+		  :todo-keyword nil :todo-type nil :scheduled nil :deadline nil
+		  :timestamp nil :clock nil :post-blank 0 :category nil))))
+      ;; TODO keyword.
+      (should
+       (equal
+	"TODO"
+	(let ((org-todo-keywords '((sequence "TODO" "DONE"))))
+	  (org-test-with-temp-text "*************** TODO Task"
+	    (org-element-property
+	     :todo-keyword
+	     (org-element-map
+	      (org-element-parse-buffer) 'inlinetask 'identity nil t))))))
+      ;; Planning info.
+      (should
+       (equal
+	"2012-03-29 thu."
+	(org-test-with-temp-text "
+*************** Task
+DEADLINE: <2012-03-29 thu.>"
+	  (org-element-property
+	   :deadline
+	   (org-element-map
+	    (org-element-parse-buffer) 'inlinetask 'identity nil t)))))
+      ;; Priority.
+      (should
+       (equal
+	?A
+	(org-test-with-temp-text "
+*************** [#A] Task"
+	  (org-element-property
+	   :priority
+	   (org-element-map
+	    (org-element-parse-buffer) 'inlinetask 'identity nil t)))))
+      ;; Tags.
+      (should
+       (equal
+	'("test")
+	(org-test-with-temp-text "
+*************** Task :test:"
+	  (org-element-property
+	   :tags
+	   (org-element-map
+	    (org-element-parse-buffer) 'inlinetask 'identity nil t))))))))
+
+
+;;;; Item.
+
+(ert-deftest test-org-element/item-parser ()
+  "Test `item' parser."
+  ;; Standard test.
+  (should
+   (equal
+    (org-test-with-temp-text "- item"
+      (org-element-map (org-element-parse-buffer) 'item 'identity nil t))
+    '(item
+      (:bullet "- " :begin 1 :end 7 :contents-begin 3 :contents-end 7
+	       :checkbox nil :counter nil :tag nil :hiddenp nil
+	       :structure ((1 0 "- " nil nil nil 7))
+	       :post-blank 0)
+      (paragraph
+       (:begin 3 :end 7 :contents-begin 3 :contents-end 7 :post-blank 0)
+       "item"))))
+  ;; Counter.
+  (should
+   (= 6
+      (org-element-property
+       :counter
+       (org-test-with-temp-text "6. [@6] item"
+	 (org-element-map (org-element-parse-buffer) 'item 'identity nil t)))))
+  ;; Tag
+  (should
+   (equal
+    '("tag")
+    (org-element-property
+     :tag
+     (org-test-with-temp-text "- tag :: description"
+       (org-element-map (org-element-parse-buffer) 'item 'identity nil t)))))
+  ;; Check-boxes
+  (should
+   (equal
+    '(trans on off)
+    (org-test-with-temp-text "
+- [-] item 1
+  - [X] item 1.1
+  - [ ] item 1.2"
+      (org-element-map
+       (org-element-parse-buffer) 'item
+       (lambda (item) (org-element-property :checkbox item))))))
+  ;; Folded state.
+  (org-test-with-temp-text "* Headline
+- item
+
+  paragraph below"
+    (forward-line)
+    (let ((org-cycle-include-plain-lists t)) (org-cycle))
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map (org-element-parse-buffer) 'item 'identity nil t)))))
+
+
+;;;; Link
 
 (ert-deftest test-org-element/link-parser ()
-  "Test link parsing."
+  "Test `link' parser."
   ;; 1. Radio target.
   (should
    (equal (org-test-with-temp-text "A radio link"
@@ -436,10 +870,162 @@ Return interpreted string."
 			:raw-link "http://orgmode.org" :begin 9 :end 29
 			:contents-begin nil :contents-end nil :post-blank 0)))))
 
-;;;; Verse blocks
+
+;;;; Plain List.
+
+(ert-deftest test-org-element/plain-list-parser ()
+  "Test `plain-list' parser."
+  (should
+   (equal
+    (org-test-with-temp-text "- item"
+      (org-element-map (org-element-parse-buffer) 'plain-list 'identity nil t))
+    '(plain-list
+      (:type unordered :begin 1 :end 7 :contents-begin 1 :contents-end 7
+	     :structure ((1 0 "- " nil nil nil 7)) :post-blank 0)
+      (item
+       (:bullet "- " :begin 1 :end 7 :contents-begin 3 :contents-end 7
+		:checkbox nil :counter nil :tag nil :hiddenp nil
+		:structure ((1 0 "- " nil nil nil 7)) :post-blank 0)
+       (paragraph
+	(:begin 3 :end 7 :contents-begin 3 :contents-end 7 :post-blank 0)
+	"item")))))
+  ;; Blank lines after the list only belong to outer plain list.
+  (org-test-with-temp-text "
+- outer
+  - inner
+
+Outside list"
+    (let ((endings (org-element-map
+		    (org-element-parse-buffer) 'plain-list
+		    (lambda (pl) (org-element-property :end pl)))))
+      ;; Move to ending of outer list.
+      (goto-char (car endings))
+      (should (looking-at "Outside list"))
+      ;; Move to ending of inner list.
+      (goto-char (nth 1 endings))
+      (should (looking-at "^$")))))
+
+
+;;;; Src Block.
+
+(ert-deftest test-org-element/src-block-parser ()
+  "Test `src-block' parser."
+  ;; Regular tests.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_SRC\nText\n#+END_SRC"
+      (org-element-map
+       (org-element-parse-buffer) 'src-block 'identity nil t))
+    '(src-block
+      (:language nil :switches nil :parameters nil :begin 1 :end 27
+		 :number-lines nil :preserve-indent nil :retain-labels t
+		 :use-labels t :label-fmt nil :hiddenp nil :value "Text\n"
+		 :post-blank 0))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_SRC\nText\n#+END_SRC"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'src-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_SRC"
+     (org-element-map
+      (org-element-parse-buffer) 'src-block 'identity nil t))))
+
+
+;;;; Quote Block
+
+(ert-deftest test-org-element/quote-block-parser ()
+  "Test `quote-block' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_QUOTE\nText\n#+END_QUOTE"
+      (org-element-map
+       (org-element-parse-buffer) 'quote-block 'identity nil t))
+    '(quote-block
+      (:begin 1 :end 31 :hiddenp nil :contents-begin 15 :contents-end 20
+	      :post-blank 0)
+      (paragraph
+       (:begin 15 :end 20 :contents-begin 15 :contents-end 19 :post-blank 0)
+       "Text"))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_QUOTE\nText\n#+END_QUOTE"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'quote-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_QUOTE"
+     (org-element-map
+      (org-element-parse-buffer) 'quote-block 'identity nil t))))
+
+
+;;;; Section
+
+(ert-deftest test-org-element/section-parser ()
+  "Test `section' parser."
+  ;; Standard test.
+  (should
+   (equal
+    (org-test-with-temp-text "* Headline\nText"
+      (org-element-map (org-element-parse-buffer) 'section 'identity nil t))
+    '(section
+      (:begin 12 :end 16 :contents-begin 12 :contents-end 16 :post-blank 0)
+      (paragraph
+       (:begin 12 :end 16 :contents-begin 12 :contents-end 16 :post-blank 0)
+       "Text"))))
+  ;; There's a section before the first headline.
+  (should
+   (org-test-with-temp-text "Text"
+     (org-element-map (org-element-parse-buffer) 'section 'identity)))
+  ;; A section cannot be empty.
+  (should-not
+   (org-test-with-temp-text "* Headline 1\n* Headline 2"
+     (org-element-map (org-element-parse-buffer) 'section 'identity))))
+
+
+;;;; Special Block
+
+(ert-deftest test-org-element/special-block-parser ()
+  "Test `special-block' parser."
+  ;; Regular test.
+  (should
+   (equal
+    (org-test-with-temp-text "#+BEGIN_SPECIAL\nText\n#+END_SPECIAL"
+      (org-element-map
+       (org-element-parse-buffer) 'special-block 'identity nil t))
+    '(special-block
+      (:type "SPECIAL" :begin 1 :end 35 :hiddenp nil :contents-begin 17
+	     :contents-end 22 :post-blank 0)
+      (paragraph
+       (:begin 17 :end 22 :contents-begin 17 :contents-end 21 :post-blank 0)
+       "Text"))))
+  ;; Test folded block.
+  (org-test-with-temp-text "#+BEGIN_SPECIAL\nText\n#+END_SPECIAL"
+    (org-cycle)
+    (should
+     (org-element-property
+      :hiddenp
+      (org-element-map
+       (org-element-parse-buffer) 'special-block 'identity nil t))))
+  ;; Ignore incomplete block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_SPECIAL"
+     (org-element-map
+      (org-element-parse-buffer) 'special-block 'identity nil t))))
+
+
+;;;; Verse Block
 
 (ert-deftest test-org-element/verse-block-parser ()
-  "Test verse block parsing."
+  "Test `verse-block' parser."
   ;; Standard test.
   (org-test-with-temp-text "#+BEGIN_VERSE\nVerse block\n#+END_VERSE"
     (should
@@ -470,7 +1056,12 @@ Return interpreted string."
 	"Verse block\n"))))
   ;; Parse objects in verse blocks.
   (org-test-with-temp-text "#+BEGIN_VERSE\nVerse \\alpha\n#+END_VERSE"
-    (should (org-element-map (org-element-parse-buffer) 'entity 'identity))))
+    (should (org-element-map (org-element-parse-buffer) 'entity 'identity)))
+  ;; Ignore incomplete verse block.
+  (should-not
+   (org-test-with-temp-text "#+BEGIN_VERSE"
+     (org-element-map
+      (org-element-parse-buffer) 'verse-block 'identity nil t))))