From 6b91eb9e657ab0a91c2df6d0081b302af20f083a Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Fri, 2 Mar 2012 22:37:12 +0100 Subject: [PATCH] org-footnote: Fix normalization of inline footnotes with no footnote section * lisp/org-footnote.el (org-footnote-normalize): Fix normalization of inline footnotes with no footnote section. * testing/lisp/test-org-footnote.el: New test file. Thanks to Samuel Wales for reporting this. --- lisp/org-footnote.el | 42 +++--- testing/lisp/test-org-footnote.el | 260 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 19 deletions(-) create mode 100644 testing/lisp/test-org-footnote.el diff --git a/lisp/org-footnote.el b/lisp/org-footnote.el index 25376f586..71c010148 100644 --- a/lisp/org-footnote.el +++ b/lisp/org-footnote.el @@ -676,8 +676,7 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': ;; If EXPORT-PROPS isn't nil, also add `org-footnote' ;; property to it, so it can be easily recognized by ;; exporters. - (if sort-only - (goto-char (nth 2 ref)) + (if sort-only (goto-char (nth 2 ref)) (delete-region (nth 1 ref) (nth 2 ref)) (goto-char (nth 1 ref)) (let ((new-ref (format "[%d]" marker))) @@ -706,7 +705,10 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': '(:todo-keywords t :tags t :priority t)))) (org-export-preprocess-string def parameters)) def) - inlinep pos) ref-table))))) + ;; Reference beginning position is a marker + ;; to preserve it during further buffer + ;; modifications. + inlinep (copy-marker pos)) ref-table))))) ;; 2. Find and remove the footnote section, if any. Also ;; determine where footnotes shall be inserted (INS-POINT). (cond @@ -722,10 +724,9 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': (skip-chars-backward " \r\t\n") (forward-line) (unless (bolp) (newline))) - ;; No footnote section set: Footnotes will be added before next - ;; headline. - ((eq major-mode 'org-mode) - (org-with-limited-levels (outline-next-heading))) + ;; No footnote section set: Footnotes will be added at the end + ;; of the section containing their first reference. + ((eq major-mode 'org-mode)) (t ;; Remove any left-over tag in the buffer, if one is set up. (when org-footnote-tag-for-non-org-mode-files @@ -758,18 +759,21 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': (lambda (x) (cond ;; When only sorting, ignore inline footnotes. - ((and sort-only (nth 3 x)) nil) + ;; Also clear position marker. + ((and sort-only (nth 3 x)) + (set-marker (nth 4 x) nil) nil) ;; No definition available: provide one. ((not (nth 2 x)) - (append (butlast x 2) - (list (format "DEFINITION NOT FOUND: %s" (car x)) - (nth 3 x)))) + (append + (list (car x) (nth 1 x) + (format "DEFINITION NOT FOUND: %s" (car x))) + (nthcdr 3 x))) (t x))) ref-table))) (setq ref-table (nreverse ref-table)) ;; 4. Remove left-over definitions in the buffer. - (mapc (lambda (x) (unless (nth 3 x) - (org-footnote-delete-definitions (car x)))) + (mapc (lambda (x) + (unless (nth 3 x) (org-footnote-delete-definitions (car x)))) ref-table) ;; 5. Insert the footnotes again in the buffer, at the ;; appropriate spot. @@ -791,11 +795,6 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': (skip-chars-backward " \t\n\r") (delete-region (point) ins-point) (unless (bolp) (newline)) - ;; Keep one blank line between footnotes and signature. - (when (and (derived-mode-p 'message-mode) - (save-excursion - (re-search-forward message-signature-separator nil t))) - (open-line 1)) (when org-footnote-tag-for-non-org-mode-files (insert "\n" org-footnote-tag-for-non-org-mode-files "\n"))) ((and org-footnote-section (not export-props)) @@ -808,6 +807,8 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': (insert (mapconcat (lambda (x) + ;; Clean markers. + (set-marker (nth 4 x) nil) (format "\n[%s] %s" (nth (if sort-only 0 1) x) (nth 2 x))) ref-table "\n")) (unless (eobp) (insert "\n\n")) @@ -819,7 +820,10 @@ Additional note on `org-footnote-insert-pos-for-preprocessor': ((not sort-only) (mapc (lambda (x) - (goto-char (nth 4 x)) + (let ((pos (nth 4 x))) + (goto-char pos) + ;; Clean marker. + (set-marker pos nil)) (org-footnote-goto-local-insertion-point) (insert (format "\n[%s] %s\n" (nth 1 x) (nth 2 x)))) ref-table)) diff --git a/testing/lisp/test-org-footnote.el b/testing/lisp/test-org-footnote.el new file mode 100644 index 000000000..52e553808 --- /dev/null +++ b/testing/lisp/test-org-footnote.el @@ -0,0 +1,260 @@ +;;; test-org-footnote.el --- Tests for org-footnote.el + +;; Copyright (C) 2012 Nicolas Goaziou + +;; Author: Nicolas Goaziou + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Code: + +(ert-deftest test-org-footnote/normalize-in-org () + "Test specifications for `org-footnote-normalize' in an Org buffer." + ;; 1. With a non-nil `org-footnote-section'. + (let ((org-footnote-section "Footnotes") + (org-blank-before-new-entry '((heading . auto)))) + ;; 1.1. Normalize each type of footnote: standard, labelled, + ;; numbered, inline, anonymous. + (org-test-with-temp-text + "Paragraph[fn:1][fn:label][1][fn:inline:Inline][fn::Anonymous] + +* Footnotes + +\[fn:1] Standard + +\[fn:label] Labelled + +\[1] Numbered" + (org-footnote-normalize) + (should + (equal (buffer-string) + "Paragraph[1][2][3][4][5] + +* Footnotes + +\[1] Standard + +\[2] Labelled + +\[3] Numbered + +\[4] Inline + +\[5] Anonymous + + +"))) + ;; 1.2. When no footnote section is present, create it. Follow + ;; `org-blank-before-new-entry' specifications when doing so. + (org-test-with-temp-text "Paragraph[fn:1]\n\n[fn:1] Definition" + (org-footnote-normalize) + (should (equal (buffer-string) + "Paragraph[1]\n\n* Footnotes\n\n[1] Definition"))) + (org-test-with-temp-text "Paragraph[fn:1]\n* Head1\n[fn:1] Definition" + (let ((org-blank-before-new-entry '((heading)))) + (org-footnote-normalize)) + (should (equal (buffer-string) + "Paragraph[1]\n* Head1\n* Footnotes\n\n[1] Definition"))) + ;; 1.3. When the footnote section is misplaced, move it at the end + ;; of the buffer. + (org-test-with-temp-text "* Head1 +Body[fn:1] +* Footnotes +\[fn:1] Definition 1 +* Head2" + (org-footnote-normalize) + (should + (equal (buffer-string) + "* Head1 +Body[1] +* Head2 + +* Footnotes + +\[1] Definition 1")))) + ;; 2. With a nil `org-footnote-section'. + (let ((org-footnote-section nil)) + ;; 2.1. Normalize each type of footnote: standard, labelled, + ;; numbered, inline, anonymous. + (org-test-with-temp-text + "Paragraph[fn:1][fn:label][1][fn:inline:Inline][fn::Anonymous] + +\[fn:1] Standard + +\[fn:label] Labelled + +\[1] Numbered" + (org-footnote-normalize) + (should + (equal (buffer-string) + "Paragraph[1][2][3][4][5] + +\[1] Standard + +\[2] Labelled + +\[3] Numbered + +\[4] Inline + +\[5] Anonymous + +"))) + ;; 2.2. Put each footnote definition at the end of the section + ;; containing its first reference. + (org-test-with-temp-text + "* Head 1 +Text[fn:1:Def1] +* Head 2 +Text[fn:1] +* Head 3 +Text[fn:2:Def2]" + (org-footnote-normalize) + (should + (equal (buffer-string) + "* Head 1 +Text[1] + +\[1] Def1 +* Head 2 +Text[1] +* Head 3 +Text[2] + +\[2] Def2 +"))))) + +(ert-deftest test-org-footnote/normalize-outside-org () + "Test `org-footnote-normalize' specifications for buffers not in Org mode." + ;; 1. In a non-Org buffer, footnotes definitions are always put at + ;; its end. + (let ((org-footnote-tag-for-non-org-mode-files nil)) + (with-temp-buffer + (insert "Paragraph[fn:1][fn:label][1][fn:inline:Inline][fn::Anonymous] + +\[fn:1] Standard + +\[fn:label] Labelled + +\[1] Numbered + +Some additional text.") + (org-footnote-normalize) + (should + (equal (buffer-string) + "Paragraph[1][2][3][4][5] + +Some additional text. + +\[1] Standard + +\[2] Labelled + +\[3] Numbered + +\[4] Inline + +\[5] Anonymous")))) + ;; 2. With a special tag. + (let ((org-footnote-tag-for-non-org-mode-files "Footnotes:")) + ;; 2.1. The tag must be inserted before the footnotes, separated + ;; from the rest of the text with a blank line. + (with-temp-buffer + (insert "Paragraph[fn:1][fn::Anonymous] + +\[fn:1] Standard + +Some additional text.") + (org-footnote-normalize) + (should + (equal (buffer-string) + "Paragraph[1][2] + +Some additional text. + +Footnotes: + +\[1] Standard + +\[2] Anonymous"))) + ;; 2.2. Any tag already inserted in the buffer should be removed + ;; prior to footnotes insertion. + (with-temp-buffer + (insert "Text[fn:1] +Footnotes: + +Additional text. + +Footnotes: + +\[fn:1] Definition") + (org-footnote-normalize) + (should + (equal (buffer-string) + "Text[1] + +Additional text. + +Footnotes: + +\[1] Definition")))) + ;; 3. As an exception, in `message-mode' buffer, if a signature is + ;; present, insert footnotes before it. + (let ((org-footnote-tag-for-non-org-mode-files nil) + (message-signature-separator "^-- $")) + (with-temp-buffer + (insert "Body[fn::def] +-- +Fake signature +-- +Signature") + (let ((major-mode 'message-mode)) (org-footnote-normalize)) + (should + (equal (buffer-string) + "Body[1] +-- +Fake signature + +\[1] def + +-- +Signature"))))) + +(ert-deftest test-org-footnote/sort () + "Test footnotes definitions sorting." + (let ((org-footnote-section nil)) + (org-test-with-temp-text + "Text[fn:1][fn::inline][fn:2][fn:label] + +\[fn:label] C + +\[fn:1] A + +\[fn:2] B" + (org-footnote-normalize 'sort) + (should + (equal (buffer-string) + "Text[fn:1][fn::inline][fn:2][fn:label] + +\[fn:1] A + +\[fn:2] B + +\[fn:label] C + +"))))) + + +(provide 'test-org-footnote) +;;; test-org-footnote.el ends here -- 2.11.4.GIT