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.
This commit is contained in:
Nicolas Goaziou 2012-05-04 19:24:51 +02:00
parent e7dda77691
commit d83ab52626
2 changed files with 651 additions and 75 deletions

View File

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

View File

@ -36,10 +36,118 @@ Return interpreted string."
;;; Test Parsers ;;; 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 () (ert-deftest test-org-element/comment-parser ()
"Test `comment' parsing." "Test `comment' parser."
;; Regular comment. ;; Regular comment.
(should (should
(equal (equal
@ -64,10 +172,159 @@ Return interpreted string."
(org-test-with-temp-text "#+ First part\n#+ \n#+\n#+ Second part" (org-test-with-temp-text "#+ First part\n#+ \n#+\n#+ Second part"
(org-element-map (org-element-parse-buffer) 'comment 'identity nil t)) (org-element-map (org-element-parse-buffer) 'comment 'identity nil t))
'(comment '(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)))))
;;;; Example-blocks and Src-blocks ;;;; 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))))
;;;; 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 () (ert-deftest test-org-element/block-switches ()
"Test `example-block' and `src-block' switches parsing." "Test `example-block' and `src-block' switches parsing."
@ -169,10 +426,38 @@ Return interpreted string."
(equal (org-element-property :label-fmt element) "[ref:%s]")))))) (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 () (ert-deftest test-org-element/export-snippet-parser ()
"Test export-snippet parsing." "Test `export-snippet' parser."
(should (should
(equal (equal
(org-test-with-temp-text "<back-end@contents>" (org-test-with-temp-text "<back-end@contents>"
@ -183,7 +468,7 @@ Return interpreted string."
:value "contents" :begin 1 :end 20 :post-blank 0))))) :value "contents" :begin 1 :end 20 :post-blank 0)))))
;;;; Fixed width ;;;; Fixed Width
(ert-deftest test-org-element/fixed-width () (ert-deftest test-org-element/fixed-width ()
"Test fixed-width area parsing." "Test fixed-width area parsing."
@ -220,10 +505,27 @@ Return interpreted string."
(org-element-parse-buffer) 'fixed-width 'identity)))))) (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 () (ert-deftest test-org-element/footnote-reference-parser ()
"Test footnote-reference parsing." "Test `footnote-reference' parser."
;; 1. Parse a standard reference. ;; 1. Parse a standard reference.
(org-test-with-temp-text "[fn:label]" (org-test-with-temp-text "[fn:label]"
(should (equal (org-element-footnote-reference-parser) (should (equal (org-element-footnote-reference-parser)
@ -346,10 +648,142 @@ Return interpreted string."
(should (equal (org-element-property :tags headline) '("test"))))))) (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 () (ert-deftest test-org-element/link-parser ()
"Test link parsing." "Test `link' parser."
;; 1. Radio target. ;; 1. Radio target.
(should (should
(equal (org-test-with-temp-text "A radio link" (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 :raw-link "http://orgmode.org" :begin 9 :end 29
:contents-begin nil :contents-end nil :post-blank 0))))) :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 () (ert-deftest test-org-element/verse-block-parser ()
"Test verse block parsing." "Test `verse-block' parser."
;; Standard test. ;; Standard test.
(org-test-with-temp-text "#+BEGIN_VERSE\nVerse block\n#+END_VERSE" (org-test-with-temp-text "#+BEGIN_VERSE\nVerse block\n#+END_VERSE"
(should (should
@ -470,7 +1056,12 @@ Return interpreted string."
"Verse block\n")))) "Verse block\n"))))
;; Parse objects in verse blocks. ;; Parse objects in verse blocks.
(org-test-with-temp-text "#+BEGIN_VERSE\nVerse \\alpha\n#+END_VERSE" (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))))