Browse Source

Merge branch 'master' of orgmode.org:org-mode

Bastien Guerry 11 years ago
parent
commit
671a01c765
5 changed files with 113 additions and 63 deletions
  1. 3 0
      etc/ORG-NEWS
  2. 33 24
      lisp/org-element.el
  3. 10 18
      lisp/org.el
  4. 28 20
      lisp/ox.el
  5. 39 1
      testing/lisp/test-ox.el

+ 3 - 0
etc/ORG-NEWS

@@ -108,6 +108,9 @@ increased.  See manual for details.
 Inline source code was used to be removed upon exporting.  They are
 Inline source code was used to be removed upon exporting.  They are
 now handled as standard code blocks, i.e., the source code can appear
 now handled as standard code blocks, i.e., the source code can appear
 in the output, depending on the parameters.
 in the output, depending on the parameters.
+*** Extend ~org-export-first-sibling-p~ and ~org-export-last-sibling-p~
+These functions now support any element or object, not only headlines.
+*** New function: ~org-export-table-row-in-header-p~
 ** Miscellaneous
 ** Miscellaneous
 *** File names in links accept are now compatible with URI syntax
 *** File names in links accept are now compatible with URI syntax
 Absolute file names can now start with =///= in addition to =/=. E.g.,
 Absolute file names can now start with =///= in addition to =/=. E.g.,

+ 33 - 24
lisp/org-element.el

@@ -73,9 +73,9 @@
 ;; refers to the element or object containing it.  Greater elements,
 ;; refers to the element or object containing it.  Greater elements,
 ;; elements and objects containing objects will also have
 ;; elements and objects containing objects will also have
 ;; `:contents-begin' and `:contents-end' properties to delimit
 ;; `:contents-begin' and `:contents-end' properties to delimit
-;; contents.  Eventually, greater elements and elements accepting
-;; affiliated keywords will have a `:post-affiliated' property,
-;; referring to the buffer position after all such keywords.
+;; contents.  Eventually, All elements have a `:post-affiliated'
+;; property referring to the buffer position after all affiliated
+;; keywords, if any, or to their beginning position otherwise.
 ;;
 ;;
 ;; At the lowest level, a `:parent' property is also attached to any
 ;; At the lowest level, a `:parent' property is also attached to any
 ;; string, as a text property.
 ;; string, as a text property.
@@ -777,7 +777,8 @@ containing `:raw-value', `:title', `:alt-title', `:begin',
 `:end', `:pre-blank', `:contents-begin' and `:contents-end',
 `:end', `:pre-blank', `:contents-begin' and `:contents-end',
 `:level', `:priority', `:tags', `:todo-keyword',`:todo-type',
 `:level', `:priority', `:tags', `:todo-keyword',`:todo-type',
 `:scheduled', `:deadline', `:closed', `:archivedp', `:commentedp'
 `:scheduled', `:deadline', `:closed', `:archivedp', `:commentedp'
-`:footnote-section-p' and `:post-blank' keywords.
+`:footnote-section-p', `:post-blank' and `:post-affiliated'
+keywords.
 
 
 The plist also contains any property set in the property drawer,
 The plist also contains any property set in the property drawer,
 with its name in upper cases and colons added at the
 with its name in upper cases and colons added at the
@@ -902,7 +903,8 @@ Assume point is at beginning of the headline."
 				       end)
 				       end)
 			  :footnote-section-p footnote-section-p
 			  :footnote-section-p footnote-section-p
 			  :archivedp archivedp
 			  :archivedp archivedp
-			  :commentedp commentedp)
+			  :commentedp commentedp
+			  :post-affiliated begin)
 		    time-props
 		    time-props
 		    standard-props))))
 		    standard-props))))
 	(let ((alt-title (org-element-property :ALT_TITLE headline)))
 	(let ((alt-title (org-element-property :ALT_TITLE headline)))
@@ -972,7 +974,7 @@ Return a list whose CAR is `inlinetask' and CDR is a plist
 containing `:title', `:begin', `:end', `:contents-begin' and
 containing `:title', `:begin', `:end', `:contents-begin' and
 `:contents-end', `:level', `:priority', `:raw-value', `:tags',
 `:contents-end', `:level', `:priority', `:raw-value', `:tags',
 `:todo-keyword', `:todo-type', `:scheduled', `:deadline',
 `:todo-keyword', `:todo-type', `:scheduled', `:deadline',
-`:closed' and `:post-blank' keywords.
+`:closed', `:post-blank' and `:post-affiliated' keywords.
 
 
 The plist also contains any property set in the property drawer,
 The plist also contains any property set in the property drawer,
 with its name in upper cases and colons added at the
 with its name in upper cases and colons added at the
@@ -1076,7 +1078,8 @@ Assume point is at beginning of the inline task."
 			 :tags tags
 			 :tags tags
 			 :todo-keyword todo
 			 :todo-keyword todo
 			 :todo-type todo-type
 			 :todo-type todo-type
-			 :post-blank (count-lines before-blank end))
+			 :post-blank (count-lines before-blank end)
+			 :post-affiliated begin)
 		   time-props
 		   time-props
 		   standard-props))))
 		   standard-props))))
       (org-element-put-property
       (org-element-put-property
@@ -1135,8 +1138,8 @@ STRUCT is the structure of the plain list.
 
 
 Return a list whose CAR is `item' and CDR is a plist containing
 Return a list whose CAR is `item' and CDR is a plist containing
 `:bullet', `:begin', `:end', `:contents-begin', `:contents-end',
 `:bullet', `:begin', `:end', `:contents-begin', `:contents-end',
-`:checkbox', `:counter', `:tag', `:structure' and `:post-blank'
-keywords.
+`:checkbox', `:counter', `:tag', `:structure', `:post-blank' and
+`:post-affiliated' keywords.
 
 
 When optional argument RAW-SECONDARY-P is non-nil, item's tag, if
 When optional argument RAW-SECONDARY-P is non-nil, item's tag, if
 any, will not be parsed as a secondary string, but as a plain
 any, will not be parsed as a secondary string, but as a plain
@@ -1195,7 +1198,8 @@ Assume point is at the beginning of the item."
 			:checkbox checkbox
 			:checkbox checkbox
 			:counter counter
 			:counter counter
 			:structure struct
 			:structure struct
-			:post-blank (count-lines contents-end end)))))
+			:post-blank (count-lines contents-end end)
+			:post-affiliated begin))))
       (org-element-put-property
       (org-element-put-property
        item :tag
        item :tag
        (let ((raw-tag (org-list-get-tag begin struct)))
        (let ((raw-tag (org-list-get-tag begin struct)))
@@ -1483,8 +1487,8 @@ CONTENTS is the contents of the element."
 LIMIT bounds the search.
 LIMIT bounds the search.
 
 
 Return a list whose CAR is `section' and CDR is a plist
 Return a list whose CAR is `section' and CDR is a plist
-containing `:begin', `:end', `:contents-begin', `contents-end'
-and `:post-blank' keywords."
+containing `:begin', `:end', `:contents-begin', `contents-end',
+`:post-blank' and `:post-affiliated' keywords."
   (save-excursion
   (save-excursion
     ;; Beginning of section is the beginning of the first non-blank
     ;; Beginning of section is the beginning of the first non-blank
     ;; line after previous headline.
     ;; line after previous headline.
@@ -1499,7 +1503,8 @@ and `:post-blank' keywords."
 		  :end end
 		  :end end
 		  :contents-begin begin
 		  :contents-begin begin
 		  :contents-end pos-before-blank
 		  :contents-end pos-before-blank
-		  :post-blank (count-lines pos-before-blank end))))))
+		  :post-blank (count-lines pos-before-blank end)
+		  :post-affiliated begin)))))
 
 
 (defun org-element-section-interpreter (section contents)
 (defun org-element-section-interpreter (section contents)
   "Interpret SECTION element as Org syntax.
   "Interpret SECTION element as Org syntax.
@@ -1629,8 +1634,8 @@ CONTENTS is nil."
 LIMIT bounds the search.
 LIMIT bounds the search.
 
 
 Return a list whose CAR is `clock' and CDR is a plist containing
 Return a list whose CAR is `clock' and CDR is a plist containing
-`:status', `:value', `:time', `:begin', `:end' and `:post-blank'
-as keywords."
+`:status', `:value', `:time', `:begin', `:end', `:post-blank' and
+`:post-affiliated' as keywords."
   (save-excursion
   (save-excursion
     (let* ((case-fold-search nil)
     (let* ((case-fold-search nil)
 	   (begin (point))
 	   (begin (point))
@@ -1654,7 +1659,8 @@ as keywords."
 		  :duration duration
 		  :duration duration
 		  :begin begin
 		  :begin begin
 		  :end end
 		  :end end
-		  :post-blank post-blank)))))
+		  :post-blank post-blank
+		  :post-affiliated begin)))))
 
 
 (defun org-element-clock-interpreter (clock contents)
 (defun org-element-clock-interpreter (clock contents)
   "Interpret CLOCK element as Org syntax.
   "Interpret CLOCK element as Org syntax.
@@ -2095,8 +2101,8 @@ CONTENTS is nil."
 LIMIT bounds the search.
 LIMIT bounds the search.
 
 
 Return a list whose CAR is `node-property' and CDR is a plist
 Return a list whose CAR is `node-property' and CDR is a plist
-containing `:key', `:value', `:begin', `:end' and `:post-blank'
-keywords."
+containing `:key', `:value', `:begin', `:end', `:post-blank' and
+`:post-affiliated' keywords."
   (save-excursion
   (save-excursion
     (looking-at org-property-re)
     (looking-at org-property-re)
     (let ((case-fold-search t)
     (let ((case-fold-search t)
@@ -2111,7 +2117,8 @@ keywords."
 		  :value value
 		  :value value
 		  :begin begin
 		  :begin begin
 		  :end end
 		  :end end
-		  :post-blank (count-lines pos-before-blank end))))))
+		  :post-blank (count-lines pos-before-blank end)
+		  :post-affiliated begin)))))
 
 
 (defun org-element-node-property-interpreter (node-property contents)
 (defun org-element-node-property-interpreter (node-property contents)
   "Interpret NODE-PROPERTY element as Org syntax.
   "Interpret NODE-PROPERTY element as Org syntax.
@@ -2227,8 +2234,8 @@ CONTENTS is the contents of the element."
 LIMIT bounds the search.
 LIMIT bounds the search.
 
 
 Return a list whose CAR is `planning' and CDR is a plist
 Return a list whose CAR is `planning' and CDR is a plist
-containing `:closed', `:deadline', `:scheduled', `:begin', `:end'
-and `:post-blank' keywords."
+containing `:closed', `:deadline', `:scheduled', `:begin',
+`:end', `:post-blank' and `:post-affiliated' keywords."
   (save-excursion
   (save-excursion
     (let* ((case-fold-search nil)
     (let* ((case-fold-search nil)
 	   (begin (point))
 	   (begin (point))
@@ -2254,7 +2261,8 @@ and `:post-blank' keywords."
 		  :scheduled scheduled
 		  :scheduled scheduled
 		  :begin begin
 		  :begin begin
 		  :end end
 		  :end end
-		  :post-blank post-blank)))))
+		  :post-blank post-blank
+		  :post-affiliated begin)))))
 
 
 (defun org-element-planning-interpreter (planning contents)
 (defun org-element-planning-interpreter (planning contents)
   "Interpret PLANNING element as Org syntax.
   "Interpret PLANNING element as Org syntax.
@@ -2470,7 +2478,7 @@ LIMIT bounds the search.
 
 
 Return a list whose CAR is `table-row' and CDR is a plist
 Return a list whose CAR is `table-row' and CDR is a plist
 containing `:begin', `:end', `:contents-begin', `:contents-end',
 containing `:begin', `:end', `:contents-begin', `:contents-end',
-`:type' and `:post-blank' keywords."
+`:type', `:post-blank' and `:post-affiliated' keywords."
   (save-excursion
   (save-excursion
     (let* ((type (if (looking-at "^[ \t]*|-") 'rule 'standard))
     (let* ((type (if (looking-at "^[ \t]*|-") 'rule 'standard))
 	   (begin (point))
 	   (begin (point))
@@ -2491,7 +2499,8 @@ containing `:begin', `:end', `:contents-begin', `:contents-end',
 		  :end end
 		  :end end
 		  :contents-begin contents-begin
 		  :contents-begin contents-begin
 		  :contents-end contents-end
 		  :contents-end contents-end
-		  :post-blank 0)))))
+		  :post-blank 0
+		  :post-affiliated begin)))))
 
 
 (defun org-element-table-row-interpreter (table-row contents)
 (defun org-element-table-row-interpreter (table-row contents)
   "Interpret TABLE-ROW element as Org syntax.
   "Interpret TABLE-ROW element as Org syntax.

+ 10 - 18
lisp/org.el

@@ -22380,7 +22380,7 @@ ELEMENT."
 	      (if level (1+ level) 0))))
 	      (if level (1+ level) 0))))
 	 ((item plain list)
 	 ((item plain list)
 	  (org-list-item-body-column
 	  (org-list-item-body-column
-	   (or (org-element-property :post-affiliated element) start)))
+	   (org-element-property :post-affiliated element)))
 	 (otherwise
 	 (otherwise
 	  (goto-char start)
 	  (goto-char start)
 	  (org-get-indentation))))
 	  (org-get-indentation))))
@@ -22512,8 +22512,7 @@ Also align node properties according to `org-property-format'."
 	   (type (org-element-type element)))
 	   (type (org-element-type element)))
       (cond ((and (memq type '(plain-list item))
       (cond ((and (memq type '(plain-list item))
 		  (= (line-beginning-position)
 		  (= (line-beginning-position)
-		     (or (org-element-property :post-affiliated element)
-			 (org-element-property :begin element))))
+		     (org-element-property :post-affiliated element)))
 	     'noindent)
 	     'noindent)
 	    ((and (eq type 'src-block)
 	    ((and (eq type 'src-block)
 		  org-src-tab-acts-natively
 		  org-src-tab-acts-natively
@@ -22582,8 +22581,7 @@ assumed to be significant there."
 	      ;; according to the element type, or not indented at
 	      ;; according to the element type, or not indented at
 	      ;; all.  Other parts are indented as a single block.
 	      ;; all.  Other parts are indented as a single block.
 	      (let* ((post (copy-marker
 	      (let* ((post (copy-marker
-			    (or (org-element-property :post-affiliated element)
-				(org-element-property :begin element))))
+			    (org-element-property :post-affiliated element)))
 		     (cbeg
 		     (cbeg
 		      (copy-marker
 		      (copy-marker
 		       (cond
 		       (cond
@@ -22744,7 +22742,7 @@ matches in paragraphs or comments, use it."
 			 (org-element-at-point)))
 			 (org-element-at-point)))
 	      (type (org-element-type element))
 	      (type (org-element-type element))
 	      (post-affiliated (org-element-property :post-affiliated element)))
 	      (post-affiliated (org-element-property :post-affiliated element)))
-	 (unless (and post-affiliated (< p post-affiliated))
+	 (unless (< p post-affiliated)
 	   (case type
 	   (case type
 	     (comment
 	     (comment
 	      (save-excursion
 	      (save-excursion
@@ -22753,10 +22751,7 @@ matches in paragraphs or comments, use it."
 		(concat (match-string 0) "# ")))
 		(concat (match-string 0) "# ")))
 	     (footnote-definition "")
 	     (footnote-definition "")
 	     ((item plain-list)
 	     ((item plain-list)
-	      (make-string (org-list-item-body-column
-			    (or post-affiliated
-				(org-element-property :begin element)))
-			   ?\s))
+	      (make-string (org-list-item-body-column post-affiliated) ?\s))
 	     (paragraph
 	     (paragraph
 	      ;; Fill prefix is usually the same as the current line,
 	      ;; Fill prefix is usually the same as the current line,
 	      ;; unless the paragraph is at the beginning of an item.
 	      ;; unless the paragraph is at the beginning of an item.
@@ -22989,9 +22984,7 @@ region only contains such lines."
            ((and (memq type '(babel-call clock comment diary-sexp headline
            ((and (memq type '(babel-call clock comment diary-sexp headline
 					 horizontal-rule keyword paragraph
 					 horizontal-rule keyword paragraph
 					 planning))
 					 planning))
-		 (or (not (org-element-property :post-affiliated element))
-		     (<= (org-element-property :post-affiliated element)
-			 (point))))
+		 (<= (org-element-property :post-affiliated element) (point)))
             (skip-chars-forward " \t")
             (skip-chars-forward " \t")
             (insert ": "))
             (insert ": "))
            ((and (org-looking-at-p "[ \t]*$")
            ((and (org-looking-at-p "[ \t]*$")
@@ -23946,7 +23939,7 @@ item, etc.  It also provides some special moves for convenience:
            (skip-chars-forward " \r\t\n")
            (skip-chars-forward " \r\t\n")
            (or (eobp) (beginning-of-line)))
            (or (eobp) (beginning-of-line)))
           ;; On affiliated keywords, move to element's beginning.
           ;; On affiliated keywords, move to element's beginning.
-          ((and post-affiliated (< (point) post-affiliated))
+          ((< (point) post-affiliated)
            (goto-char post-affiliated))
            (goto-char post-affiliated))
           ;; At a table row, move to the end of the table.  Similarly,
           ;; At a table row, move to the end of the table.  Similarly,
           ;; at a node property, move to the end of the property
           ;; at a node property, move to the end of the property
@@ -24025,7 +24018,7 @@ convenience:
      ((= (point) begin)
      ((= (point) begin)
       (backward-char)
       (backward-char)
       (org-backward-paragraph))
       (org-backward-paragraph))
-     ((and post-affiliated (<= (point) post-affiliated)) (goto-char begin))
+     ((<= (point) post-affiliated) (goto-char begin))
      ((memq type '(node-property table-row))
      ((memq type '(node-property table-row))
       (goto-char (org-element-property
       (goto-char (org-element-property
                   :post-affiliated (org-element-property :parent element))))
                   :post-affiliated (org-element-property :parent element))))
@@ -24521,7 +24514,7 @@ To get rid of the restriction, use \\[org-agenda-remove-restriction-lock]."
 		   (otherwise t)))))))
 		   (otherwise t)))))))
       (cond
       (cond
        ;; Ignore checks in all affiliated keywords but captions.
        ;; Ignore checks in all affiliated keywords but captions.
-       ((and post-affiliated (< (point) post-affiliated))
+       ((< (point) post-affiliated)
 	(and (save-excursion
 	(and (save-excursion
 	       (beginning-of-line)
 	       (beginning-of-line)
 	       (let ((case-fold-search t)) (looking-at "[ \t]*#\\+CAPTION:")))
 	       (let ((case-fold-search t)) (looking-at "[ \t]*#\\+CAPTION:")))
@@ -24544,8 +24537,7 @@ To get rid of the restriction, use \\[org-agenda-remove-restriction-lock]."
 	  ((comment quote-section) t)
 	  ((comment quote-section) t)
 	  (comment-block
 	  (comment-block
 	   ;; Allow checks between block markers, not on them.
 	   ;; Allow checks between block markers, not on them.
-	   (and (> (line-beginning-position)
-		   (org-element-property :post-affiliated element))
+	   (and (> (line-beginning-position) post-affiliated)
 		(save-excursion
 		(save-excursion
 		  (end-of-line)
 		  (end-of-line)
 		  (skip-chars-forward " \r\t\n")
 		  (skip-chars-forward " \r\t\n")

+ 28 - 20
lisp/ox.el

@@ -3887,16 +3887,19 @@ title is defined, fall-back to the regular title."
   (or (org-element-property :alt-title headline)
   (or (org-element-property :alt-title headline)
       (org-element-property :title headline)))
       (org-element-property :title headline)))
 
 
-(defun org-export-first-sibling-p (headline info)
-  "Non-nil when HEADLINE is the first sibling in its sub-tree.
-INFO is a plist used as a communication channel."
-  (not (eq (org-element-type (org-export-get-previous-element headline info))
-	   'headline)))
+(defun org-export-first-sibling-p (blob info)
+  "Non-nil when BLOB is the first sibling in its parent.
+BLOB is an element or an object.  If BLOB is a headline, non-nil
+means it is the first sibling in the sub-tree.  INFO is a plist
+used as a communication channel."
+  (memq (org-element-type (org-export-get-previous-element blob info))
+	'(nil section)))
 
 
-(defun org-export-last-sibling-p (headline info)
-  "Non-nil when HEADLINE is the last sibling in its sub-tree.
-INFO is a plist used as a communication channel."
-  (not (org-export-get-next-element headline info)))
+(defun org-export-last-sibling-p (blob info)
+  "Non-nil when BLOB is the last sibling in its parent.
+BLOB is an element or an object.  INFO is a plist used as
+a communication channel."
+  (not (org-export-get-next-element blob info)))
 
 
 
 
 ;;;; For Keywords
 ;;;; For Keywords
@@ -4431,9 +4434,10 @@ code."
 ;; `org-export-table-cell-ends-colgroup-p',
 ;; `org-export-table-cell-ends-colgroup-p',
 ;; `org-export-table-row-starts-rowgroup-p',
 ;; `org-export-table-row-starts-rowgroup-p',
 ;; `org-export-table-row-ends-rowgroup-p',
 ;; `org-export-table-row-ends-rowgroup-p',
-;; `org-export-table-row-starts-header-p' and
-;; `org-export-table-row-ends-header-p' indicate position of current
-;; row or cell within the table.
+;; `org-export-table-row-starts-header-p',
+;; `org-export-table-row-ends-header-p' and
+;; `org-export-table-row-in-header-p' indicate position of current row
+;; or cell within the table.
 
 
 (defun org-export-table-has-special-column-p (table)
 (defun org-export-table-has-special-column-p (table)
   "Non-nil when TABLE has a special column.
   "Non-nil when TABLE has a special column.
@@ -4787,21 +4791,25 @@ INFO is a plist used as a communication channel."
 		    (car (org-element-contents table-row)) info)))
 		    (car (org-element-contents table-row)) info)))
       (or (memq 'bottom borders) (memq 'below borders)))))
       (or (memq 'bottom borders) (memq 'below borders)))))
 
 
+(defun org-export-table-row-in-header-p (table-row info)
+  "Non-nil when TABLE-ROW is located within table's header.
+INFO is a plist used as a communication channel.  Always return
+nil for special rows and rows separators."
+  (and (org-export-table-has-header-p
+	(org-export-get-parent-table table-row) info)
+       (eql (org-export-table-row-group table-row info) 1)))
+
 (defun org-export-table-row-starts-header-p (table-row info)
 (defun org-export-table-row-starts-header-p (table-row info)
   "Non-nil when TABLE-ROW is the first table header's row.
   "Non-nil when TABLE-ROW is the first table header's row.
 INFO is a plist used as a communication channel."
 INFO is a plist used as a communication channel."
-  (and (org-export-table-has-header-p
-	(org-export-get-parent-table table-row) info)
-       (org-export-table-row-starts-rowgroup-p table-row info)
-       (= (org-export-table-row-group table-row info) 1)))
+  (and (org-export-table-row-in-header-p table-row info)
+       (org-export-table-row-starts-rowgroup-p table-row info)))
 
 
 (defun org-export-table-row-ends-header-p (table-row info)
 (defun org-export-table-row-ends-header-p (table-row info)
   "Non-nil when TABLE-ROW is the last table header's row.
   "Non-nil when TABLE-ROW is the last table header's row.
 INFO is a plist used as a communication channel."
 INFO is a plist used as a communication channel."
-  (and (org-export-table-has-header-p
-	(org-export-get-parent-table table-row) info)
-       (org-export-table-row-ends-rowgroup-p table-row info)
-       (= (org-export-table-row-group table-row info) 1)))
+  (and (org-export-table-row-in-header-p table-row info)
+       (org-export-table-row-ends-rowgroup-p table-row info)))
 
 
 (defun org-export-table-row-number (table-row info)
 (defun org-export-table-row-number (table-row info)
   "Return TABLE-ROW number.
   "Return TABLE-ROW number.

+ 39 - 1
testing/lisp/test-ox.el

@@ -1719,10 +1719,23 @@ Paragraph[fn:1]"
   (should
   (should
    (equal
    (equal
     '(yes yes no)
     '(yes yes no)
-    (org-test-with-parsed-data "* Headline\n** Headline 2\n** Headline 3"
+    (org-test-with-parsed-data "* H\n** H 2\n** H 3"
       (org-element-map tree 'headline
       (org-element-map tree 'headline
 	(lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no))
 	(lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no))
 	info))))
 	info))))
+  (should
+   (equal '(yes no)
+	  (org-test-with-parsed-data "- item\n\n  para"
+	    (org-element-map tree 'paragraph
+	      (lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no))
+	      info))))
+  ;; Ignore sections for headlines.
+  (should
+   (equal '(yes yes)
+	  (org-test-with-parsed-data "* H\nSection\n** H 2"
+	    (org-element-map tree 'headline
+	      (lambda (h) (if (org-export-first-sibling-p h info) 'yes 'no))
+	      info))))
   ;; Ignore headlines not exported.
   ;; Ignore headlines not exported.
   (should
   (should
    (equal
    (equal
@@ -1743,6 +1756,12 @@ Paragraph[fn:1]"
       (org-element-map tree 'headline
       (org-element-map tree 'headline
 	(lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no))
 	(lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no))
 	info))))
 	info))))
+  (should
+   (equal '(no yes)
+	  (org-test-with-parsed-data "- item\n\n  para"
+	    (org-element-map tree 'paragraph
+	      (lambda (h) (if (org-export-last-sibling-p h info) 'yes 'no))
+	      info))))
   ;; Ignore headlines not exported.
   ;; Ignore headlines not exported.
   (should
   (should
    (equal
    (equal
@@ -2893,6 +2912,25 @@ Another text. (ref:text)
 	  (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no))
 	  (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no))
 	info)))))
 	info)))))
 
 
+(ert-deftest test-org-export/table-row-in-header-p ()
+  "Test `org-export-table-row-in-header-p' specifications."
+  ;; Standard test.  Separators are always nil.
+  (should
+   (equal
+    '(yes no no)
+    (org-test-with-parsed-data "| a |\n|---|\n| b |"
+      (org-element-map tree 'table-row
+	(lambda (row)
+	  (if (org-export-table-row-in-header-p row info) 'yes 'no)) info))))
+  ;; Nil when there is no header.
+  (should
+   (equal
+    '(no no)
+    (org-test-with-parsed-data "| a |\n| b |"
+      (org-element-map tree 'table-row
+	(lambda (row)
+	  (if (org-export-table-row-in-header-p row info) 'yes 'no)) info)))))
+
 (ert-deftest test-org-export/table-row-starts-header-p ()
 (ert-deftest test-org-export/table-row-starts-header-p ()
   "Test `org-export-table-row-starts-header-p' specifications."
   "Test `org-export-table-row-starts-header-p' specifications."
   ;; 1. Only the row starting the first row group starts the table
   ;; 1. Only the row starting the first row group starts the table