소스 검색

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.
Nicolas Goaziou 13 년 전
부모
커밋
6b91eb9e65
2개의 변경된 파일283개의 추가작업 그리고 19개의 파일을 삭제
  1. 23 19
      lisp/org-footnote.el
  2. 260 0
      testing/lisp/test-org-footnote.el

+ 23 - 19
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))

+ 260 - 0
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 <n.goaziou at gmail dot com>
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; 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