Browse Source

ox: Allow to narrow scope in footnotes API

* lisp/ox.el (org-export--footnote-reference-map,
  org-export-collect-footnote-definitions,
  org-export-footnote-first-reference-p,
  org-export-get-footnote-number): Allow to specify scope, through
  a new optional argument.

* lisp/ox-odt.el (org-odt-footnote-reference): Apply API change.

* testing/lisp/test-ox.el (test-org-export/footnote-first-reference-p,
  test-org-export/get-footnote-number,
  test-org-export/collect-footnote-definitions): Update tests.
Nicolas Goaziou 3 years ago
parent
commit
fa145361ec
3 changed files with 88 additions and 19 deletions
  1. 3 2
      lisp/ox-odt.el
  2. 20 10
      lisp/ox.el
  3. 65 7
      testing/lisp/test-ox.el

+ 3 - 2
lisp/ox-odt.el

@@ -1740,9 +1740,10 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
 	    (format "<text:span text:style-name=\"%s\">%s</text:span>"
 		    "OrgSuperscript" ",")))
      ;; Transcode footnote reference.
-     (let ((n (org-export-get-footnote-number footnote-reference info t)))
+     (let ((n (org-export-get-footnote-number footnote-reference info nil t)))
        (cond
-	((not (org-export-footnote-first-reference-p footnote-reference info t))
+	((not
+	  (org-export-footnote-first-reference-p footnote-reference info nil t))
 	 (funcall --format-footnote-reference n))
 	;; Inline definitions are secondary strings.
 	;; Non-inline footnotes definitions are full Org data.

+ 20 - 10
lisp/ox.el

@@ -3556,8 +3556,9 @@ definition can be found, raise an error."
 	  (org-element-contents footnote-reference))
 	(error "Definition not found for footnote %s" label))))
 
-(defun org-export--footnote-reference-map (function info &optional body-first)
-  "Apply FUNCTION on every footnote reference in parse tree.
+(defun org-export--footnote-reference-map
+    (function data info &optional body-first)
+  "Apply FUNCTION on every footnote reference in DATA.
 INFO is a plist containing export state.  By default, as soon as
 a new footnote reference is encountered, FUNCTION is called onto
 its definition.  However, if BODY-FIRST is non-nil, this step is
@@ -3597,15 +3598,18 @@ delayed until the end of the process."
 	      ;; definitions of inline references.
 	      (if delayp '(footnote-definition footnote-reference)
 		'footnote-definition)))))
-    (funcall search-ref (plist-get info :parse-tree) body-first)
+    (funcall search-ref data body-first)
     (funcall search-ref (nreverse definitions) nil)))
 
-(defun org-export-collect-footnote-definitions (info &optional body-first)
+(defun org-export-collect-footnote-definitions (info &optional data body-first)
   "Return an alist between footnote numbers, labels and definitions.
 
 INFO is the current export state, as a plist.
 
-Definitions are sorted by order of references.  As soon as a new
+Definitions are collected throughout the whole parse tree, or
+DATA when non-nil.
+
+Sorting is done by order of references.  As soon as a new
 reference is encountered, other references are searched within
 its definition.  However, if BODY-FIRST is non-nil, this step is
 delayed after the whole tree is checked.  This alters results
@@ -3623,16 +3627,19 @@ for inlined footnotes.  Unreferenced definitions are ignored."
 	   (incf n)
 	   (push (list n l d) alist))
 	 (when l (push l labels))))
-     info body-first)
+     (or data (plist-get info :parse-tree)) info body-first)
     (nreverse alist)))
 
 (defun org-export-footnote-first-reference-p
-    (footnote-reference info &optional body-first)
+    (footnote-reference info &optional data body-first)
   "Non-nil when a footnote reference is the first one for its label.
 
 FOOTNOTE-REFERENCE is the footnote reference being considered.
 INFO is a plist containing current export state.
 
+Search is done throughout the whole parse tree, or DATA when
+non-nil.
+
 By default, as soon as a new footnote reference is encountered,
 other references are searched within its definition.  However, if
 BODY-FIRST is non-nil, this step is delayed after the whole tree
@@ -3647,14 +3654,17 @@ footnote definitions."
 	     (let ((l (org-element-property :label f)))
 	       (when (and l label (string= label l))
 		 (throw 'exit (eq footnote-reference f)))))
-	   info body-first)))))
+	   (or data (plist-get info :parse-tree)) info body-first)))))
 
-(defun org-export-get-footnote-number (footnote info &optional body-first)
+(defun org-export-get-footnote-number (footnote info &optional data body-first)
   "Return number associated to a footnote.
 
 FOOTNOTE is either a footnote reference or a footnote definition.
 INFO is the plist containing export state.
 
+Number is unique throughout the whole parse tree, or DATA, when
+non-nil.
+
 By default, as soon as a new footnote reference is encountered,
 counting process moves into its definition.  However, if
 BODY-FIRST is non-nil, this step is delayed until the end of the
@@ -3675,7 +3685,7 @@ process, leading to a different order when footnotes are nested."
 	    ;; wasn't encountered yet.
 	    ((not l) (incf count))
 	    ((not (member l seen)) (push l seen) (incf count)))))
-       info body-first))))
+       (or data (plist-get info :parse-tree)) info body-first))))
 
 
 ;;;; For Headlines

+ 65 - 7
testing/lisp/test-ox.el

@@ -1557,6 +1557,30 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (paragraph . (lambda (p c i) c))))
 	 nil nil nil '(:with-footnotes t))
 	(nreverse result)))))
+  ;; Limit check to DATA, when non-nil.
+  (should
+   (equal
+    '(nil t)
+    (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:1]\n\n[fn:1] D1"
+      (let (result)
+	(org-element-map tree 'footnote-reference
+	  (lambda (ref)
+	    (push
+	     (org-export-footnote-first-reference-p
+	      ref info (org-element-map tree 'headline #'identity info t))
+	     result))
+	  info)
+	(nreverse result)))))
+  (should
+   (equal
+    '(t nil)
+    (org-test-with-parsed-data "Text[fn:1]\n* H\nText[fn:1]\n\n[fn:1] D1"
+      (let (result)
+	(org-element-map tree 'footnote-reference
+	  (lambda (ref)
+	    (push (org-export-footnote-first-reference-p ref info) result))
+	  info)
+	(nreverse result)))))
   ;; If optional argument BODY-FIRST is non-nil, first find footnote
   ;; in the main body of the document.  Otherwise, enter footnote
   ;; definitions when they are encountered.
@@ -1593,7 +1617,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	  `(,(cons 'footnote-reference
 		   (lambda (f c i)
 		     (when (org-element-lineage f '(drawer))
-		       (push (org-export-footnote-first-reference-p f info t)
+		       (push (org-export-footnote-first-reference-p f info nil t)
 			     result))
 		     ""))
 	    (drawer . (lambda (d c i) c))
@@ -1631,8 +1655,27 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	  (cons (org-export-get-footnote-number ref info)
 		(org-element-property :label ref)))
 	info))))
-  ;; With a non-nil optional argument, first check body, then footnote
-  ;; definitions.
+  ;; Limit number to provided DATA, when non-nil.
+  (should
+   (equal
+    '(1)
+    (org-test-with-parsed-data
+	"Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2"
+      (org-element-map tree 'footnote-reference
+	(lambda (ref)
+	  (org-export-get-footnote-number
+	   ref info (org-element-map tree 'headline #'identity info t)))
+	info))))
+  (should
+   (equal
+    '(1 2)
+    (org-test-with-parsed-data
+	"Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2]"
+      (org-element-map tree 'footnote-reference
+	(lambda (ref) (org-export-get-footnote-number ref info))
+	info))))
+  ;; With a non-nil BODY-FIRST optional argument, first check body,
+  ;; then footnote definitions.
   (should
    (equal
     '(("fn:1" . 1) ("fn:2" . 2) ("fn:3" . 3) ("fn:3" . 3))
@@ -1641,7 +1684,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
       (org-element-map tree 'footnote-reference
 	(lambda (ref)
 	  (cons (org-element-property :label ref)
-		(org-export-get-footnote-number ref info t)))
+		(org-export-get-footnote-number ref info nil t)))
 	info))))
   (should
    (equal
@@ -1664,8 +1707,23 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 
 \[fn:3] C."
 	(length (org-export-collect-footnote-definitions info)))))
-  ;; With optional argument, first check body, then footnote
-  ;; definitions.
+  ;; Limit number to provided DATA, when non-nil.
+  (should
+   (equal
+    '((1 "fn:2"))
+    (org-test-with-parsed-data
+	"Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2"
+      (mapcar #'butlast
+	      (org-export-collect-footnote-definitions
+	       info (org-element-map tree 'headline #'identity info t))))))
+  (should
+   (equal
+    '((1 "fn:1") (2 "fn:2"))
+    (org-test-with-parsed-data
+	"Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2"
+      (mapcar #'butlast (org-export-collect-footnote-definitions info)))))
+  ;; With optional argument BODY-FIRST, first check body, then
+  ;; footnote definitions.
   (should
    (equal '("fn:1" "fn:3" "fn:2" nil)
 	  (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
@@ -1674,7 +1732,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 
 \[fn:3] C."
 	    (mapcar (lambda (e) (nth 1 e))
-		    (org-export-collect-footnote-definitions info t)))))
+		    (org-export-collect-footnote-definitions info nil t)))))
   (should-not
    (equal '("fn:1" "fn:3" "fn:2" nil)
 	  (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].