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.
This commit is contained in:
Nicolas Goaziou 2015-12-16 18:38:53 +01:00
parent deafe56554
commit 8eb318f2d0
2 changed files with 926 additions and 771 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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.
(should
(equal "Paragraph[1][2][3][4][5]
* Footnotes
\[1] Standard
\[2] Labelled
\[3] Numbered
\[4] Inline
\[5] Anonymous
"
(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]
* Footnotes
\[fn:1] Standard
\[fn:label] Labelled
\[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.
(should
(equal
"* Head1
Body[1]
* Head2
* 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]
* 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
\[2] Labelled
\[3] Numbered
\[4] Inline
\[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] Standard
\[fn:label] Labelled
\[1] Numbered"
(org-footnote-normalize)
(buffer-string)))))
;; Also put each footnote definition at the end of the section
(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 "* Head 1
Text[1]
(equal
"
Text[fn:1][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: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:2] Def 2
\[fn:1] Def 1"
(let ((org-footnote-section nil)) (org-footnote-sort))
(buffer-string))))
(should
(equal
"Paragraph[1][2][3][4][5]
"
* H1
Text[fn:1]
\[fn:1] Def 1
* H2
Text[fn:2]
Some additional text.
\[fn:2] Def 2
"
(org-test-with-temp-text "
* H1
Text[fn:1]
* H2
Text[fn:2]
\[1] Standard
\[fn:1] Def 1
\[2] Labelled
\[fn:2] Def 2
"
(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]
\[3] Numbered
* Footnotes
\[4] Inline
\[fn:1] Def 1
\[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
"
(org-test-with-temp-text "
Text[fn:1][fn:2]
\[fn:1] Standard
\[fn:2] Def 2
\[fn:label] Labelled
\[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
"
Text[fn:1]
\[1] Numbered
* Headline
* Other headline
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]
* Footnotes
\[fn:1] Standard
\[fn:1] Def 1
"
(org-test-with-temp-text "
Text[fn:1]
* Footnotes
Some additional text.")
(org-footnote-normalize)
(should
(equal (buffer-string)
"Paragraph[1][2]
\[fn:1] Def 1
* Headline
Some additional text.
** Footnotes
Footnotes:
* Other headline"
(let ((org-footnote-section "Footnotes")) (org-footnote-sort))
(buffer-string))))
;; Ignore anonymous footnotes.
(should
(equal
"
Text[fn:1][fn::inline][fn:2]
\[1] Standard
\[fn:1] Def 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:
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.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
\[1] def
--
Signature")))))
(ert-deftest test-org-footnote/sort ()
"Test footnotes definitions sorting."
(let ((org-footnote-section nil))
\[fn:2] Def 2
"
(org-test-with-temp-text
"Text[fn:1][fn::inline][fn:2][fn:label]
"
Text[fn:1][fn::inline][fn:2]
\[fn:label] C
\[fn:2] Def 2
\[fn:1] A
\[fn:1] Def 1"
(let ((org-footnote-section nil)) (org-footnote-sort))
(buffer-string))))
;; Ignore inline footnotes.
(should
(equal
"
Text[fn:1][fn:label:inline][fn:2]
\[fn:2] B"
(org-footnote-normalize 'sort)
(should
(equal (buffer-string)
"Text[fn:1][fn::inline][fn:2][fn:label]
\[fn:1] Def 1
\[fn:1] A
\[fn:2] Def 2
"
(org-test-with-temp-text
"
Text[fn:1][fn:label:inline][fn:2]
\[fn:2] B
\[fn:2] Def 2
\[fn:label] C
")))))
\[fn:1] Def 1"
(let ((org-footnote-section nil)) (org-footnote-sort))
(buffer-string))))
;; Handle (deeply) nested footnotes.
(should
(equal
"
Text[fn:1][fn:3]
\[fn:1] Def 1[fn:2]
\[fn:2] Def 2
\[fn:3] Def 3
"
(org-test-with-temp-text "
Text[fn:1][fn:3]
\[fn:1] Def 1[fn:2]
\[fn:3] Def 3
\[fn:2] Def 2
"
(let ((org-footnote-section nil)) (org-footnote-sort))
(buffer-string))))
(should
(equal
"
Text[fn:1][fn:4]
\[fn:1] Def 1[fn:2]
\[fn:2] Def 2[fn:3]
\[fn:3] Def 3
\[fn:4] Def 4
"
(org-test-with-temp-text "
Text[fn:1][fn:4]
\[fn:1] Def 1[fn:2]
\[fn:3] Def 3
\[fn:2] Def 2[fn:3]
\[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]
\[fn:1] Def 1
* Section 2
Text[fn:1]"
(org-test-with-temp-text
"
* Section 1
Text[fn:1]
\[fn:1] Def 1
* Section 2
Text[fn:1]"
(let ((org-footnote-section nil)) (org-footnote-sort))
(buffer-string))))
(should
(equal
"
Text[fn:1][fn:4]
\[fn:1] Def 1[fn:2][fn:3]
\[fn:2] Def 2[fn:3]
\[fn:3] Def 3
\[fn:4] Def 4
"
(org-test-with-temp-text "
Text[fn:1][fn:4]
\[fn:1] Def 1[fn:2][fn:3]
\[fn:3] Def 3
\[fn:2] Def 2[fn:3]
\[fn:4] Def 4
"
(let ((org-footnote-section nil)) (org-footnote-sort))
(buffer-string)))))
(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] def[fn:2]
\[fn:2] inline
\[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)