From 81cc6dff19a141271af89a20ba80359cde93a542 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 25 Feb 2012 14:51:15 +0100 Subject: [PATCH] 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. --- EXPERIMENTAL/org-e-latex.el | 52 +++++++++++++++++-------- contrib/lisp/org-element.el | 7 ++-- testing/contrib/lisp/test-org-export.el | 27 +++++++++++++ 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/EXPERIMENTAL/org-e-latex.el b/EXPERIMENTAL/org-e-latex.el index bf9cebab2..459954969 100644 --- a/EXPERIMENTAL/org-e-latex.el +++ b/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))) (when (eq (org-element-type prev) 'footnote-reference) org-e-latex-footnote-separator)) - ;; Use \footnotemark if the footnote has already been defined. - ;; Otherwise, define it with \footnote command. (cond + ;; Use \footnotemark if the footnote has already been defined. ((not (org-export-footnote-first-reference-p footnote-reference info)) - (format "\\footnotemark[%s]" + (format "\\footnotemark[%s]{}" (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 - (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 diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el index ac389f3f9..75d50f570 100644 --- a/contrib/lisp/org-element.el +++ b/contrib/lisp/org-element.el @@ -2563,9 +2563,10 @@ entities, export snippets, latex-fragments, subscript and superscript.") (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 macro radio-target statistics-cookie sub/superscript text-markup time-stamp) diff --git a/testing/contrib/lisp/test-org-export.el b/testing/contrib/lisp/test-org-export.el index 6d05d54bf..401975b0b 100644 --- a/testing/contrib/lisp/test-org-export.el +++ b/testing/contrib/lisp/test-org-export.el @@ -315,3 +315,30 @@ body\n"))) (let ((org-export-filter-parse-tree-functions '(skip-note-head))) (org-test-with-temp-text "* Head1\n* Head2 (note)\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)))))))