Преглед на файлове

org-e-latex: Allow nested footnotes

* EXPERIMENTAL/org-e-latex.el (org-e-latex-footnote-reference): Allow
  nested footnotes.
* contrib/lisp/org-element.el (org-element-string-restrictions): Allow
  footnote references within a footnote reference.
* testing/contrib/lisp/test-org-export.el: Add a test for nested
  footnotes.
Nicolas Goaziou преди 13 години
родител
ревизия
81cc6dff19
променени са 3 файла, в които са добавени 67 реда и са изтрити 19 реда
  1. 36 16
      EXPERIMENTAL/org-e-latex.el
  2. 4 3
      contrib/lisp/org-element.el
  3. 27 0
      testing/contrib/lisp/test-org-export.el

+ 36 - 16
EXPERIMENTAL/org-e-latex.el

@@ -1013,26 +1013,46 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
    (let ((prev (org-export-get-previous-element footnote-reference info)))
    (let ((prev (org-export-get-previous-element footnote-reference info)))
      (when (eq (org-element-type prev) 'footnote-reference)
      (when (eq (org-element-type prev) 'footnote-reference)
        org-e-latex-footnote-separator))
        org-e-latex-footnote-separator))
-   ;; Use \footnotemark if the footnote has already been defined.
-   ;; Otherwise, define it with \footnote command.
    (cond
    (cond
+    ;; Use \footnotemark if the footnote has already been defined.
     ((not (org-export-footnote-first-reference-p footnote-reference info))
     ((not (org-export-footnote-first-reference-p footnote-reference info))
-     (format "\\footnotemark[%s]"
+     (format "\\footnotemark[%s]{}"
 	     (org-export-get-footnote-number footnote-reference info)))
 	     (org-export-get-footnote-number footnote-reference info)))
-    ;; Inline definitions are secondary strings.
-    ((eq (org-element-property :type footnote-reference) 'inline)
-     (format "\\footnote{%s}"
-	     (org-trim
-	      (org-export-secondary-string
-	       (org-export-get-footnote-definition footnote-reference info)
-	       'e-latex info))))
-    ;; Non-inline footnotes definitions are full Org data.
+    ;; Use also \footnotemark if reference is within another footnote
+    ;; reference or footnote definition.
+    ((loop for parent in (org-export-get-genealogy footnote-reference info)
+	   thereis (memq (org-element-type parent)
+			 '(footnote-reference footnote-definition)))
+     (format "\\footnotemark[%s]{}"
+	     (org-export-get-footnote-number footnote-reference info)))
+    ;; Otherwise, define it with \footnote command.
     (t
     (t
-     (format "\\footnote{%s}"
-	     (org-trim
-	      (org-export-data
-	       (org-export-get-footnote-definition footnote-reference info)
-	       'e-latex info)))))))
+     (let ((def (org-export-get-footnote-definition footnote-reference info)))
+       (unless (eq (org-element-type def) 'org-data)
+	 (setq def (cons 'org-data (cons nil def))))
+       (concat
+	(format "\\footnote{%s}" (org-trim (org-export-data def 'e-latex info)))
+	;; Retrieve all footnote references within the footnote to add
+	;; their definition after it, since LaTeX doesn't support them
+	;; inside.
+	(let ((all-refs
+	       (org-element-map
+		def 'footnote-reference
+		(lambda (ref)
+		  (when (org-export-footnote-first-reference-p ref info) ref))
+		info)))
+	  (mapconcat
+	   (lambda (ref)
+	     (format
+	      "\\footnotetext[%s]{%s}"
+	      (org-export-get-footnote-number ref info)
+	      (org-trim
+	       (funcall
+		(if (org-element-property :inline-definition ref)
+		    'org-export-secondary-string
+		  'org-export-data)
+		(org-export-get-footnote-definition ref info) 'e-latex info))))
+	   all-refs ""))))))))
 
 
 
 
 ;;;; Headline
 ;;;; Headline

+ 4 - 3
contrib/lisp/org-element.el

@@ -2563,9 +2563,10 @@ entities, export snippets, latex-fragments, subscript and
 superscript.")
 superscript.")
 
 
 (defconst org-element-string-restrictions
 (defconst org-element-string-restrictions
-  '((footnote-reference entity export-snippet inline-babel-call inline-src-block
-			latex-fragment line-break link macro radio-target
-			sub/superscript target text-markup time-stamp)
+  '((footnote-reference entity export-snippet footnote-reference
+			inline-babel-call inline-src-block latex-fragment
+			line-break link macro radio-target sub/superscript
+			target text-markup time-stamp)
     (headline entity inline-babel-call inline-src-block latex-fragment link
     (headline entity inline-babel-call inline-src-block latex-fragment link
 	      macro radio-target statistics-cookie sub/superscript text-markup
 	      macro radio-target statistics-cookie sub/superscript text-markup
 	      time-stamp)
 	      time-stamp)

+ 27 - 0
testing/contrib/lisp/test-org-export.el

@@ -315,3 +315,30 @@ body\n")))
       (let ((org-export-filter-parse-tree-functions '(skip-note-head)))
       (let ((org-export-filter-parse-tree-functions '(skip-note-head)))
 	(org-test-with-temp-text "* Head1\n* Head2 (note)\n"
 	(org-test-with-temp-text "* Head1\n* Head2 (note)\n"
 	  (should (equal (org-export-as 'test) "* Head1\n")))))))
 	  (should (equal (org-export-as 'test) "* Head1\n")))))))
+
+(ert-deftest test-org-export/footnotes ()
+  "Test footnotes specifications."
+  ;; 1. Test nested footnotes.
+  (let ((org-footnote-section nil))
+    (org-test-with-temp-text "
+Some text[fn:1] and some other text[fn:new:and an inline
+footnote with another one[fn:label:reference to[fn:1] and a new
+one[fn:label2:label2]].
+
+[fn:1] with a footnote inside[fn:inside] and a new footnote [fn:label3:label3].
+
+[fn:inside] like that."
+(let* ((tree (org-element-parse-buffer))
+       (info (org-combine-plists
+	      (org-export-initial-options) '(:with-footnotes t))))
+  (setq info (org-combine-plists
+	      info (org-export-collect-tree-properties tree info 'test)))
+  (let* ((fn-numbers
+	  (org-element-map
+	   tree 'footnote-reference
+	   (lambda (ref)
+	     (or (org-export-get-footnote-number ref info) 'unknown)) info)))
+    ;; 1.1. Every nested footnote has a number.
+    (should (every 'numberp fn-numbers))
+    ;; 1.2. Can tell which are new and which aren't.
+    (should (= (apply 'max fn-numbers) 5)))))))