Browse Source

org-footnote: Update library wrt new footnote syntax

* lisp/org-footnote.el (org-footnote-re):
(org-footnote-definition-re): Do not match [1]-like constructs.

(org-footnote): Fix typo.

(org-footnote-auto-label): Do not offer to create [1]-like constructs
anymore.

(org-footnote-new): Remove reference to obsolete value in
`org-footnote-auto-label'.

(org-footnote-at-reference-p):
(org-footnote-get-next-reference):
(org-footnote-next-reference-or-definition):
(org-footnote-goto-definition):
(org-footnote-goto-previous-reference): Use new regexp.

(org-footnote-normalize-label): Remove "fn:" prefix instead of adding
it.

(org-footnote-get-definition):
(org-footnote-all-labels):
(org-footnote-unique-label): Small refactoring.

(org-footnote-create-definition):
(org-footnote-delete-definitions):

(org-footnote--clear-footnote-section):
(org-footnote--collect-references):
(org-footnote--collect-definitions):
(org-footnote--set-label):
(org-footnote-sort): New functions.

(org-footnote-auto-adjust-maybe):
(org-footnote-action): Use new functions.  Small refactoring.

(org-footnote-renumber-fn:N): Refactor code.  Handle nested footnotes.

(org-footnote-normalize): Turn footnotes into [fn:N] construct instead
of [N].
* testing/lisp/test-org-footnote.el (test-org-footnote/delete):

(test-org-footnote/goto-definition):
(test-org-footnote/normalize): Update test

(test-org-footnote/sort):
(test-org-footnote/renumber-fn:N): New tests.

(test-org-footnote/normalize-outside-org): Remove test.
Nicolas Goaziou 5 years ago
parent
commit
8eb318f2d0
2 changed files with 818 additions and 659 deletions
  1. 520 451
      lisp/org-footnote.el
  2. 298 208
      testing/lisp/test-org-footnote.el

File diff suppressed because it is too large
+ 520 - 451
lisp/org-footnote.el


+ 298 - 208
testing/lisp/test-org-footnote.el

@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 2012-2015  Nicolas Goaziou
 
-;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
+;; Author: Nicolas Goaziou <mail at nicolasgoaziou dot fr>
 
 ;; 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
@@ -30,15 +30,6 @@
 	    (org-footnote-section nil))
 	(org-footnote-new))
       (buffer-string))))
-  ;; `org-footnote-auto-label' is `plain'.
-  (should
-   (string-match-p
-    "Test\\[1\\]\n+\\[1\\]"
-    (org-test-with-temp-text "Test<point>"
-      (let ((org-footnote-auto-label 'plain)
-	    (org-footnote-section nil))
-	(org-footnote-new))
-      (buffer-string))))
   ;; `org-footnote-auto-label' is `random'.
   (should
    (string-match-p
@@ -128,334 +119,429 @@
   ;; Regular test.
   (should
    (equal "Paragraph"
-	  (org-test-with-temp-text "Paragraph[1]\n\n[1] Definition"
-	    (search-forward "[")
+	  (org-test-with-temp-text "Paragraph<point>[fn:1]\n\n[fn:1] Definition"
 	    (org-footnote-delete)
 	    (org-trim (buffer-string)))))
   ;; Remove multiple definitions and references.
   (should
    (equal "Paragraph and another"
 	  (org-test-with-temp-text
-	      "Paragraph[1] and another[1]\n\n[1] def\n\n[1] def"
-	    (search-forward "[")
+	      "Paragraph<point>[fn:1] and another[fn:1]
+
+\[fn:1] def
+
+\[fn:1] def"
 	    (org-footnote-delete)
 	    (org-trim (buffer-string)))))
   ;; Delete inline footnotes and all references.
   (should
    (equal "Para and"
-	  (org-test-with-temp-text "Para[fn:label:def] and[fn:label]"
-	    (search-forward "[")
+	  (org-test-with-temp-text "Para<point>[fn:label:def] and[fn:label]"
 	    (org-footnote-delete)
 	    (org-trim (buffer-string)))))
   ;; Delete anonymous footnotes.
   (should
    (equal "Para"
-	  (org-test-with-temp-text "Para[fn::def]"
-	    (search-forward "[")
-	    (org-footnote-delete)
-	    (org-trim (buffer-string)))))
+	  (let ((org-footnote-section nil))
+	    (org-test-with-temp-text "Para<point>[fn::def]"
+	      (org-footnote-delete)
+	      (org-trim (buffer-string))))))
   ;; With an argument, delete footnote with specified label.
   (should
-   (equal "Paragraph[1] and another\n\n[1] def"
+   (equal "Paragraph[fn:1] and another\n\n[fn:1] def"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text
-		"Paragraph[1] and another[2]\n\n[1] def\n\n[2] def2"
+		"Paragraph[fn:1] and another[fn:2]\n\n[fn:1] def\n\n[fn:2] def2"
 	      (org-footnote-delete "2")
 	      (org-trim (buffer-string))))))
   ;; Error when no argument is specified at point is not at a footnote
   ;; reference.
   (should-error
-   (org-test-with-temp-text "Para[1]\n\n[1] Def"
+   (org-test-with-temp-text "Para[fn:1]\n\n[fn:1] Def"
      (org-footnote-delete)))
   ;; Correctly delete footnotes with multiple paragraphs.
   (should
    (equal "Para\n\n\nOutside footnote."
-	  (org-test-with-temp-text
-	      "Para[1]\n\n[1] para1\n\npara2\n\n\nOutside footnote."
-	    (org-footnote-delete "1")
-	    (org-trim (buffer-string))))))
+	  (let ((org-footnote-section nil))
+	    (org-test-with-temp-text
+		"Para[fn:1]\n\n[fn:1] para1\n\npara2\n\n\nOutside footnote."
+	      (org-footnote-delete "1")
+	      (org-trim (buffer-string)))))))
 
 (ert-deftest test-org-footnote/goto-definition ()
   "Test `org-footnote-goto-definition' specifications."
   ;; Error on unknown definitions.
   (should-error
    (org-test-with-temp-text "No footnote definition"
-     (org-footnote-goto-definition "fn:1")))
+     (org-footnote-goto-definition "1")))
   ;; Error when trying to reach a definition outside narrowed part of
   ;; buffer.
   (should-error
    (org-test-with-temp-text "Some text<point>\n[fn:1] Definition."
      (narrow-to-region (point-min) (point))
-     (org-footnote-goto-definition "fn:1")))
+     (org-footnote-goto-definition "1")))
   (should-error
    (org-test-with-temp-text "[fn:1] Definition.\n<point>Some text"
      (narrow-to-region (point) (point-max))
-     (org-footnote-goto-definition "fn:1")))
+     (org-footnote-goto-definition "1")))
   ;; Otherwise, move at the beginning of the definition, including
   ;; anonymous footnotes.
   (should
    (equal
     "Definition."
     (org-test-with-temp-text "Some text\n[fn:1] Definition."
-      (org-footnote-goto-definition "fn:1")
+      (org-footnote-goto-definition "1")
       (buffer-substring (point) (point-max)))))
   (should
    (equal
     "definition]"
     (org-test-with-temp-text "Some text[fn:label:definition]"
-      (org-footnote-goto-definition "fn:label")
+      (org-footnote-goto-definition "label")
       (buffer-substring (point) (point-max))))))
 
-(ert-deftest test-org-footnote/normalize-in-org ()
-  "Test specifications for `org-footnote-normalize' in an Org buffer."
-  ;; With a non-nil `org-footnote-section', normalize each type of
-  ;; footnote: standard, labelled, numbered, inline, anonymous.
+(ert-deftest test-org-footnote/sort ()
+  "Test `org-footnote-sort' specifications."
+  ;; Reorder definitions with a nil `org-footnote-section'.  In this
+  ;; case each definition is written at the end of the section
+  ;; containing its first reference.
   (should
-   (equal "Paragraph[1][2][3][4][5]
+   (equal
+    "
+Text[fn:1][fn:2]
 
-* Footnotes
+\[fn:1] Def 1
 
-\[1] Standard
+\[fn:2] Def 2
+"
+    (org-test-with-temp-text "
+Text[fn:1][fn:2]
 
-\[2] Labelled
+\[fn:2] Def 2
 
-\[3] Numbered
+\[fn:1] Def 1"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  (should
+   (equal
+    "
+* H1
+Text[fn:1]
 
-\[4] Inline
+\[fn:1] Def 1
+* H2
+Text[fn:2]
 
-\[5] Anonymous
+\[fn:2] Def 2
+"
+    (org-test-with-temp-text "
+* H1
+Text[fn:1]
+* H2
+Text[fn:2]
 
+\[fn:1] Def 1
 
+\[fn:2] Def 2
 "
-	  (let ((org-footnote-section "Footnotes")
-		(org-blank-before-new-entry '((heading . auto))))
-	    (org-test-with-temp-text
-		"Paragraph[fn:1][fn:label][1][fn:inline:Inline][fn::Anonymous]
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  ;; Reorder definitions with a non-nil `org-footnote-section'.
+  (should
+   (equal
+    "
+Text[fn:1][fn:2]
 
 * Footnotes
 
-\[fn:1] Standard
+\[fn:1] Def 1
+
+\[fn:2] Def 2
+"
+    (org-test-with-temp-text "
+Text[fn:1][fn:2]
 
-\[fn:label] Labelled
+\[fn:2] Def 2
 
-\[1] Numbered"
-	      (org-footnote-normalize)
-	      (buffer-string)))))
-  ;; When no footnote section is present, create it.  Follow
-  ;; `org-blank-before-new-entry' specifications when doing so.
-  (should
-   (equal "Paragraph[1]\n\n* Footnotes\n\n[1] Definition"
-	  (let ((org-footnote-section "Footnotes")
-		(org-blank-before-new-entry '((heading . auto))))
-	    (org-test-with-temp-text "Paragraph[fn:1]\n\n[fn:1] Definition"
-	      (org-footnote-normalize)
-	      (buffer-string)))))
-  (should
-   (equal
-    "Paragraph[1]\n* Head1\n* Footnotes\n\n[1] Definition"
-    (let ((org-footnote-section "Footnotes")
-	  (org-blank-before-new-entry '((heading))))
-      (org-test-with-temp-text "Paragraph[fn:1]\n* Head1\n[fn:1] Definition"
-	(org-footnote-normalize)
-	(buffer-string)))))
-  ;; When the footnote section is misplaced, move it at the end of
-  ;; the buffer.
+\[fn:1] Def 1"
+      (let ((org-footnote-section "Footnotes")) (org-footnote-sort))
+      (buffer-string))))
+  ;; When `org-footnote-section' is non-nil, clear previous footnote
+  ;; sections.
   (should
    (equal
-    "* Head1
-Body[1]
-* Head2
+    "
+Text[fn:1]
+
+* Headline
+
+* Other headline
 
 * Footnotes
 
-\[1] Definition 1"
-    (let ((org-footnote-section "Footnotes")
-	  (org-blank-before-new-entry '((heading . auto))))
-      (org-test-with-temp-text "* Head1
-Body[fn:1]
+\[fn:1] Def 1
+"
+    (org-test-with-temp-text "
+Text[fn:1]
+
 * Footnotes
-\[fn:1] Definition 1
-* Head2"
-	(org-footnote-normalize)
-	(buffer-string)))))
-  ;; With a nil `org-footnote-section', normalize each type of
-  ;; footnote: standard, labelled, numbered, inline, anonymous.
-  (should
-   (equal "Paragraph[1][2][3][4][5]
 
-\[1] Standard
+\[fn:1] Def 1
 
-\[2] Labelled
+* Headline
 
-\[3] Numbered
+** Footnotes
 
-\[4] Inline
+* Other headline"
+      (let ((org-footnote-section "Footnotes")) (org-footnote-sort))
+      (buffer-string))))
+  ;; Ignore anonymous footnotes.
+  (should
+   (equal
+    "
+Text[fn:1][fn::inline][fn:2]
 
-\[5] Anonymous
-"
-	  (let ((org-footnote-section nil))
-	    (org-test-with-temp-text
-		"Paragraph[fn:1][fn:label][1][fn:inline:Inline][fn::Anonymous]
+\[fn:1] Def 1
 
-\[fn:1] Standard
+\[fn:2] Def 2
+"
+    (org-test-with-temp-text
+	"
+Text[fn:1][fn::inline][fn:2]
 
-\[fn:label] Labelled
+\[fn:2] Def 2
 
-\[1] Numbered"
-	      (org-footnote-normalize)
-	      (buffer-string)))))
-  ;; Also put each footnote definition at the end of the section
-  ;; containing its first reference.
+\[fn:1] Def 1"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  ;; Ignore inline footnotes.
   (should
-   (equal "* Head 1
-Text[1]
+   (equal
+    "
+Text[fn:1][fn:label:inline][fn:2]
 
-\[1] Def1
-* Head 2
-Text[1]
-* Head 3
-Text[2]
+\[fn:1] Def 1
 
-\[2] Def2
+\[fn:2] Def 2
 "
-	  (let ((org-footnote-section nil))
-	    (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)
-	      (buffer-string))))))
+    (org-test-with-temp-text
+	"
+Text[fn:1][fn:label:inline][fn:2]
+
+\[fn:2] Def 2
 
-(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.
+\[fn:1] Def 1"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  ;; Handle (deeply) nested footnotes.
   (should
    (equal
-    "Paragraph[1][2][3][4][5]
-
+    "
+Text[fn:1][fn:3]
 
-Some additional text.
+\[fn:1] Def 1[fn:2]
 
-\[1] Standard
+\[fn:2] Def 2
 
-\[2] Labelled
+\[fn:3] Def 3
+"
+    (org-test-with-temp-text "
+Text[fn:1][fn:3]
 
-\[3] Numbered
+\[fn:1] Def 1[fn:2]
 
-\[4] Inline
+\[fn:3] Def 3
 
-\[5] Anonymous"
-    (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:2] Def 2
+"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  (should
+   (equal
+    "
+Text[fn:1][fn:4]
 
-\[fn:1] Standard
+\[fn:1] Def 1[fn:2]
 
-\[fn:label] Labelled
+\[fn:2] Def 2[fn:3]
 
-\[1] Numbered
+\[fn:3] Def 3
 
+\[fn:4] Def 4
+"
+    (org-test-with-temp-text "
+Text[fn:1][fn:4]
 
-Some additional text.")
-	(org-footnote-normalize)
-	(buffer-string)))))
-  ;; 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] Def 1[fn:2]
 
-\[fn:1] Standard
+\[fn:3] Def 3
 
+\[fn:2] Def 2[fn:3]
 
-Some additional text.")
-      (org-footnote-normalize)
-      (should
-       (equal (buffer-string)
-	      "Paragraph[1][2]
+\[fn:4] Def 4
+"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  ;; When multiple (nested) references are used, make sure to insert
+  ;; definition only once.
+  (should
+   (equal
+    "
+* Section 1
 
+Text[fn:1]
 
-Some additional text.
+\[fn:1] Def 1
 
-Footnotes:
+* Section 2
 
-\[1] Standard
+Text[fn:1]"
+    (org-test-with-temp-text
+	"
+* Section 1
 
-\[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:
+Text[fn:1]
 
-Additional text.
+\[fn:1] Def 1
 
-Footnotes:
+* Section 2
 
-\[fn:1] Definition")
-      (org-footnote-normalize)
-      (should
-       (equal (buffer-string)
-	      "Text[1]
+Text[fn:1]"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string))))
+  (should
+   (equal
+    "
+Text[fn:1][fn:4]
 
-Additional text.
+\[fn:1] Def 1[fn:2][fn:3]
 
-Footnotes:
+\[fn:2] Def 2[fn:3]
 
-\[1] Definition"))))
-  ;; 3. As an exception, in `message-mode' buffer, if a signature is
-  ;;    present, insert footnotes before it.n
-  (let ((org-footnote-tag-for-non-org-mode-files nil))
-    (with-temp-buffer
-      (insert "Body[fn::def]
-Fake signature
-Signature")
-      ;; Mimic `message-mode'.
-      (let ((major-mode 'message-mode)
-	    (message-cite-prefix-regexp "\\([ 	]*[_.[:word:]]+>+\\|[ 	]*[]>|]\\)+")
-	    (message-signature-separator "^-- $"))
-	(flet ((message-point-in-header-p nil nil))
-	  (org-footnote-normalize)))
-      (should
-       (equal (buffer-string)
-              "Body[1]
-Fake signature
+\[fn:3] Def 3
 
-\[1] def
+\[fn:4] Def 4
+"
+    (org-test-with-temp-text "
+Text[fn:1][fn:4]
 
-Signature")))))
+\[fn:1] Def 1[fn:2][fn:3]
 
-(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:3] Def 3
 
-\[fn:label] C
+\[fn:2] Def 2[fn:3]
 
-\[fn:1] A
+\[fn:4] Def 4
+"
+      (let ((org-footnote-section nil)) (org-footnote-sort))
+      (buffer-string)))))
 
-\[fn:2] B"
-    (org-footnote-normalize 'sort)
-    (should
-     (equal (buffer-string)
-            "Text[fn:1][fn::inline][fn:2][fn:label]
+(ert-deftest test-org-footnote/renumber-fn:N ()
+  "Test `org-footnote-renumber-fn:N' specifications."
+  ;; Renumber (inline) references and definitions.
+  (should
+   (equal
+    "Test[fn:1]"
+    (org-test-with-temp-text "Test[fn:99]"
+      (org-footnote-renumber-fn:N)
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1]\n\n[fn:1] 99"
+    (org-test-with-temp-text "Test[fn:99]\n\n[fn:99] 99"
+      (org-footnote-renumber-fn:N)
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1:99]"
+    (org-test-with-temp-text "Test[fn:99:99]"
+      (org-footnote-renumber-fn:N)
+      (buffer-string))))
+  ;; No-op if there's no numbered footnote.
+  (should
+   (equal
+    "Test[fn:label]\n\n[fn:label] Def"
+    (org-test-with-temp-text "Test[fn:label]\n\n[fn:label] Def"
+      (org-footnote-renumber-fn:N)
+      (buffer-string))))
+  ;; Definitions without a reference get the highest numbers.
+  (should
+   (equal
+    "Test[fn:1]\n[fn:1] 1\n[fn:2] 99"
+    (org-test-with-temp-text "Test[fn:1]\n[fn:1] 1\n[fn:99] 99"
+      (org-footnote-renumber-fn:N)
+      (buffer-string))))
+  ;; Sort labels in sequence.  Anonymous footnotes are ignored.
+  (should
+   (equal
+    "Test[fn:1][fn:2:def][fn:3]"
+    (org-test-with-temp-text "Test[fn:4][fn:3:def][fn:2]"
+      (org-footnote-renumber-fn:N)
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1][fn::def][fn:2]"
+    (org-test-with-temp-text "Test[fn:4][fn::def][fn:2]"
+      (org-footnote-renumber-fn:N)
+      (buffer-string)))))
+
+(ert-deftest test-org-footnote/normalize ()
+  "Test `org-footnote-normalize' specifications."
+  ;; Normalize regular, inline and anonymous references.
+  (should
+   (equal
+    "Test[fn:1]\n\n[fn:1] def\n"
+    (org-test-with-temp-text "Test[fn:label]\n[fn:label] def"
+      (let ((org-footnote-section nil)) (org-footnote-normalize))
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1]\n\n[fn:1] def\n"
+    (org-test-with-temp-text "Test[fn:label:def]"
+      (let ((org-footnote-section nil)) (org-footnote-normalize))
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1]\n\n[fn:1] def\n"
+    (org-test-with-temp-text "Test[fn::def]"
+      (let ((org-footnote-section nil)) (org-footnote-normalize))
+      (buffer-string))))
+  ;; Normalization includes sorting.
+  (should
+   (equal
+    "Test[fn:1][fn:2]\n\n[fn:1] def2\n\n[fn:2] def\n"
+    (org-test-with-temp-text "Test[fn:2][fn:1]\n\n[fn:2] def2\n[fn:1] def"
+      (let ((org-footnote-section nil)) (org-footnote-normalize))
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1][fn:2]\n\n[fn:1] def\n\n[fn:2] inline\n"
+    (org-test-with-temp-text "Test[fn:2][fn::inline]\n[fn:2] def\n"
+      (let ((org-footnote-section nil)) (org-footnote-normalize))
+      (buffer-string))))
+  (should
+   (equal
+    "Test[fn:1][fn:3]
 
-\[fn:1] A
+\[fn:1] def[fn:2]
 
-\[fn:2] B
+\[fn:2] inline
 
-\[fn:label] C
-")))))
+\[fn:3] last
+"
+    (org-test-with-temp-text
+	"Test[fn:lab1][fn:lab2]\n[fn:lab1] def[fn::inline]\n[fn:lab2] last"
+      (let ((org-footnote-section nil)) (org-footnote-normalize))
+      (buffer-string))))
+  ;; When normalizing an inline reference, fill paragraph whenever the
+  ;; `org-footnote-fill-after-inline-note-extraction' is non-nil.
+  (should
+   (equal
+    "Test[fn:1] Next\n\n[fn:1] def\n"
+    (org-test-with-temp-text "Test[fn::def]\nNext"
+      (let ((org-footnote-section nil)
+	    (org-footnote-fill-after-inline-note-extraction t))
+	(org-footnote-normalize))
+      (buffer-string)))))
 
 
 (provide 'test-org-footnote)