org-element: Ignore plain footnotes

* lisp/org-element.el (org-element--set-regexps):
(org-element-footnote-definition-interpreter):
(org-element-footnote-reference-parser):
(org-element-footnote-reference-interpreter): Do not consider [1]-like
constructs as footnotes anymore.

* lisp/ox.el (org-export-expand-include-keyword):
(org-export--prepare-file-contents): Apply changes to footnotes.

* doc/org.texi (Footnotes): Remove references to plain footnotes.

* testing/lisp/test-org-element.el (test-org-element/footnote-reference-parser):
(test-org-element/footnote-reference-interpreter):
* testing/lisp/test-ox.el (test-org-export/expand-include):
(test-org-export/expand-macro):
(test-org-export/get-footnote-number):
(test-org-export/collect-footnote-definitions):
(test-org-export/footnotes):
(test-org-export/fuzzy-link): Update tests.

Since it is possible to refer unambiguously to a label without the "fn:"
prefix, the latter becomes part of the syntax and no longer part of the
label.  In particular [fn:1] and [fn:label] are labelled, respectively,
"1" and "label".
This commit is contained in:
Nicolas Goaziou 2015-12-16 18:22:27 +01:00
parent 25eb14bc2c
commit deafe56554
5 changed files with 72 additions and 72 deletions

View File

@ -1922,16 +1922,9 @@ The Org homepage[fn:1] now looks a lot better than it used to.
@end example
Org mode extends the number-based syntax to @emph{named} footnotes and
optional inline definition. Using plain numbers as markers (as
@file{footnote.el} does) is supported for backward compatibility, but not
encouraged because of possible conflicts with @LaTeX{} snippets (@pxref{Embedded
@LaTeX{}}). Here are the valid references:
optional inline definition. Here are the valid references:
@table @code
@item [1]
A plain numeric footnote marker. Compatible with @file{footnote.el}, but not
recommended because something like @samp{[1]} could easily be part of a code
snippet.
@item [fn:name]
A named footnote reference, where @code{name} is a unique label word, or, for
simplicity of automatic creation, a number.

View File

@ -151,7 +151,7 @@ specially in `org-element--object-lex'.")
;; Headlines, inlinetasks.
org-outline-regexp "\\|"
;; Footnote definitions.
"\\[\\(?:[0-9]+\\|fn:[-_[:word:]]+\\)\\]" "\\|"
"\\[fn:[-_[:word:]]+\\]" "\\|"
;; Diary sexps.
"%%(" "\\|"
"[ \t]*\\(?:"
@ -199,7 +199,12 @@ specially in `org-element--object-lex'.")
;; Objects starting with "[": regular link,
;; footnote reference, statistics cookie,
;; timestamp (inactive).
"\\[\\(?:fn:\\|\\(?:[0-9]\\|\\(?:%\\|/[0-9]*\\)\\]\\)\\|\\[\\)"
(concat "\\[\\(?:"
"fn:" "\\|"
"\\[" "\\|"
"[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" "\\|"
"[0-9]*\\(?:%\\|/[0-9]*\\)\\]"
"\\)")
;; Objects starting with "@": export snippets.
"@@"
;; Objects starting with "{": macro.
@ -836,7 +841,7 @@ Assume point is at the beginning of the footnote definition."
(defun org-element-footnote-definition-interpreter (footnote-definition contents)
"Interpret FOOTNOTE-DEFINITION element as Org syntax.
CONTENTS is the contents of the footnote-definition."
(concat (format "[%s]" (org-element-property :label footnote-definition))
(concat (format "[fn:%s]" (org-element-property :label footnote-definition))
" "
contents))
@ -2767,14 +2772,10 @@ When at a footnote reference, return a list whose car is
(when closing
(save-excursion
(let* ((begin (point))
(label
(or (org-match-string-no-properties 2)
(org-match-string-no-properties 3)
(and (match-string 1)
(concat "fn:" (org-match-string-no-properties 1)))))
(type (if (or (not label) (match-string 1)) 'inline 'standard))
(label (match-string-no-properties 1))
(inner-begin (match-end 0))
(inner-end (1- closing))
(type (if (match-end 2) 'inline 'standard))
(post-blank (progn (goto-char closing)
(skip-chars-forward " \t")))
(end (point)))
@ -2790,9 +2791,9 @@ When at a footnote reference, return a list whose car is
(defun org-element-footnote-reference-interpreter (footnote-reference contents)
"Interpret FOOTNOTE-REFERENCE object as Org syntax.
CONTENTS is its definition, when inline, or nil."
(format "[%s]"
(concat (or (org-element-property :label footnote-reference) "fn:")
(and contents (concat ":" contents)))))
(format "[fn:%s%s]"
(or (org-element-property :label footnote-reference) "")
(if contents (concat ":" contents) "")))
;;;; Inline Babel Call

View File

@ -3351,7 +3351,7 @@ storing and resolving footnotes. It is created automatically."
(unless included
(org-with-wide-buffer
(goto-char (point-max))
(maphash (lambda (k v) (insert (format "\n[%s] %s\n" k v)))
(maphash (lambda (k v) (insert (format "\n[fn:%s] %s\n" k v)))
footnotes)))))))))))
(defun org-export--inclusion-absolute-lines (file location only-contents lines)
@ -3472,7 +3472,7 @@ the included document."
(unless (eq major-mode 'org-mode)
(let ((org-inhibit-startup t)) (org-mode)))
(goto-char (point-min))
(let ((ind-str (make-string ind ? )))
(let ((ind-str (make-string ind ?\s)))
(while (not (or (eobp) (looking-at org-outline-regexp-bol)))
;; Do not move footnote definitions out of column 0.
(unless (and (looking-at org-footnote-definition-re)
@ -3508,17 +3508,14 @@ the included document."
(marker-max (point-max-marker))
(get-new-label
(lambda (label)
;; Generate new label from LABEL. If LABEL is akin to
;; [1] convert it to [fn:--ID-1]. Otherwise add "-ID-"
;; after "fn:".
(if (org-string-match-p "\\`[0-9]+\\'" label)
(format "fn:--%d-%s" id label)
(format "fn:-%d-%s" id (substring label 3)))))
;; Generate new label from LABEL by prefixing it with
;; "-ID-".
(format "-%d-%s" id label)))
(set-new-label
(lambda (f old new)
;; Replace OLD label with NEW in footnote F.
(save-excursion
(goto-char (1+ (org-element-property :begin f)))
(goto-char (+ (org-element-property :begin f) 4))
(looking-at (regexp-quote old))
(replace-match new))))
(seen-alist))

View File

@ -938,11 +938,6 @@ Some other text
(org-test-with-temp-text "Text[fn:label]"
(org-element-map
(org-element-parse-buffer) 'footnote-reference 'identity)))
;; Parse a normalized reference.
(should
(org-test-with-temp-text "Text[1]"
(org-element-map
(org-element-parse-buffer) 'footnote-reference 'identity)))
;; Parse an inline reference.
(should
(org-test-with-temp-text "Text[fn:test:def]"
@ -2893,17 +2888,15 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
(ert-deftest test-org-element/footnote-reference-interpreter ()
"Test footnote reference interpreter."
;; 1. Regular reference.
;; Regular reference.
(should (equal (org-test-parse-and-interpret "Text[fn:1]") "Text[fn:1]\n"))
;; 2. Normalized reference.
(should (equal (org-test-parse-and-interpret "Text[1]") "Text[1]\n"))
;; 3. Named reference.
;; Named reference.
(should (equal (org-test-parse-and-interpret "Text[fn:label]")
"Text[fn:label]\n"))
;; 4. Inline reference.
;; Inline reference.
(should (equal (org-test-parse-and-interpret "Text[fn:label:def]")
"Text[fn:label:def]\n"))
;; 5. Anonymous reference.
;; Anonymous reference.
(should (equal (org-test-parse-and-interpret "Text[fn::def]")
"Text[fn::def]\n")))

View File

@ -1054,7 +1054,7 @@ text
(length
(delete-dups
(let ((contents "
Footnotes[fn:1], [fn:test], [fn:test] and [fn:inline:anonymous footnote].
Footnotes[fn:1], [fn:test], [fn:test] and [fn:inline:inline footnote].
\[fn:1] Footnote 1
\[fn:test] Footnote \"test\""))
(org-test-with-temp-text-in-file contents
@ -1070,13 +1070,12 @@ Footnotes[fn:1], [fn:test], [fn:test] and [fn:inline:anonymous footnote].
(lambda (r) (org-element-property :label r)))))))))))))
;; Footnotes labels are not local to each include keyword.
(should
(= 4
(= 3
(length
(delete-dups
(let ((contents "
Footnotes[fn:1], [fn:test], [2] and [fn:inline:anonymous footnote].
Footnotes[fn:1], [fn:test] and [fn:inline:inline footnote].
\[fn:1] Footnote 1
\[2] Footnote 2
\[fn:test] Footnote \"test\""))
(org-test-with-temp-text-in-file contents
(let ((file (buffer-file-name)))
@ -1089,26 +1088,24 @@ Footnotes[fn:1], [fn:test], [2] and [fn:inline:anonymous footnote].
;; Footnotes are supported by :lines-like elements and unnecessary
;; footnotes are dropped.
(should
(= 4
(= 3
(length
(delete-dups
(let ((contents "
* foo
Footnotes[fn:1]
* bar
Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote]
Footnotes[fn:2], foot[fn:test] and [fn:inline:inline footnote]
\[fn:1] Footnote 1
\[fn:2] Footnote 1
* Footnotes
\[fn:test] Footnote \"test\"
\[3] Footnote 3
"))
(org-test-with-temp-text-in-file contents
(let ((file (buffer-file-name)))
(org-test-with-temp-text
(format "#+INCLUDE: \"%s::*bar\"
" file)
(format "#+INCLUDE: \"%s::*bar\"\n" file)
(org-export-expand-include-keyword)
(org-element-map (org-element-parse-buffer)
'footnote-definition
@ -1119,7 +1116,8 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
"body\n"
(org-test-with-temp-text
(concat
(format "#+INCLUDE: \"%s/examples/include.org::*Heading\" " org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::*Heading\" "
org-test-dir)
":only-contents t")
(org-export-expand-include-keyword)
(buffer-string))))
@ -1135,27 +1133,32 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
(equal
"| 1 |\n"
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::tbl\" :only-contents t"
org-test-dir)
(org-export-expand-include-keyword)
(buffer-string))))
;; Including non-existing elements should result in an error.
(should-error
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\"" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::*non-existing heading\""
org-test-dir)
(org-export-expand-include-keyword)))
;; Lines work relatively to an included element.
(should
(equal
"2\n3\n"
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t :lines \"2-3\"" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::#ah\" :only-contents t \
:lines \"2-3\""
org-test-dir)
(org-export-expand-include-keyword)
(buffer-string))))
;; Properties should be dropped from headlines.
(should
(equal
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::#ht\" :only-contents t"
org-test-dir)
(org-export-expand-include-keyword)
(buffer-string))
(org-test-with-temp-text
@ -1167,7 +1170,8 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
(equal
":LOGBOOK:\ndrawer\n:END:\ncontent\n"
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::#dh\" :only-contents t"
org-test-dir)
(org-export-expand-include-keyword)
(buffer-string))))
;; Adjacent INCLUDE-keywords should have the same :minlevel if unspecified.
@ -1175,8 +1179,10 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
(cl-every (lambda (level) (zerop (1- level)))
(org-test-with-temp-text
(concat
(format "#+INCLUDE: \"%s/examples/include.org::#ah\"\n" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::*Heading\"" org-test-dir))
(format "#+INCLUDE: \"%s/examples/include.org::#ah\"\n"
org-test-dir)
(format "#+INCLUDE: \"%s/examples/include.org::*Heading\""
org-test-dir))
(org-export-expand-include-keyword)
(org-element-map (org-element-parse-buffer) 'headline
(lambda (head) (org-element-property :level head))))))
@ -1184,30 +1190,37 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
(should-not
(equal
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include2.org\" src emacs-lisp" org-test-dir)
(format "#+INCLUDE: \"%s/examples/include2.org\" src emacs-lisp"
org-test-dir)
(org-export-expand-include-keyword)
(buffer-string))
(org-test-with-temp-text
(format "#+INCLUDE: \"%s/examples/include2.org\" src emacs-lisp :minlevel 1" org-test-dir)
(format
"#+INCLUDE: \"%s/examples/include2.org\" src emacs-lisp :minlevel 1"
org-test-dir)
(org-export-expand-include-keyword)
(buffer-string))))
;; INCLUDE assigns the relative :minlevel conditional on narrowing.
(should
(org-test-with-temp-text-in-file
(format "* h1\n<point>#+INCLUDE: \"%s/examples/include.org::#ah\"" org-test-dir)
(format "* h1\n<point>#+INCLUDE: \"%s/examples/include.org::#ah\""
org-test-dir)
(narrow-to-region (point) (point-max))
(org-export-expand-include-keyword)
(eq 1 (org-current-level))))
;; If :minlevel is present do not alter it.
(should
(org-test-with-temp-text
(format "* h1\n<point>#+INCLUDE: \"%s/examples/include.org::#ah\" :minlevel 3" org-test-dir)
(format
"* h1\n<point>#+INCLUDE: \"%s/examples/include.org::#ah\" :minlevel 3"
org-test-dir)
(narrow-to-region (point) (point-max))
(org-export-expand-include-keyword)
(eq 3 (org-current-level)))))
(ert-deftest test-org-export/expand-macro ()
"Test macro expansion in an Org buffer."
(require 'ox-org)
;; Standard macro expansion.
(should
(equal "#+MACRO: macro1 value\nvalue\n"
@ -1873,7 +1886,7 @@ Para2"
;; Test nested footnotes order.
(should
(equal
'((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (3 . "fn:3") (4))
'((1 . "1") (2 . "2") (3 . "3") (3 . "3") (4))
(org-test-with-parsed-data
"Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
(org-element-map tree 'footnote-reference
@ -1904,7 +1917,7 @@ Para2"
;; then footnote definitions.
(should
(equal
'(("fn:1" . 1) ("fn:2" . 2) ("fn:3" . 3) ("fn:3" . 3))
'(("1" . 1) ("2" . 2) ("3" . 3) ("3" . 3))
(org-test-with-parsed-data
"Text[fn:1][fn:2][fn:3]\n\n[fn:1] Def[fn:3]\n[fn:2] Def\n[fn:3] Def"
(org-element-map tree 'footnote-reference
@ -1914,7 +1927,7 @@ Para2"
info))))
(should
(equal
'(("fn:1" . 1) ("fn:2" . 3) ("fn:3" . 2) ("fn:3" . 2))
'(("1" . 1) ("2" . 3) ("3" . 2) ("3" . 2))
(org-test-with-parsed-data
"Text[fn:1][fn:2][fn:3]\n\n[fn:1] Def[fn:3]\n[fn:2] Def\n[fn:3] Def"
(org-element-map tree 'footnote-reference
@ -1936,7 +1949,7 @@ Para2"
;; Limit number to provided DATA, when non-nil.
(should
(equal
'((1 "fn:2"))
'((1 "2"))
(org-test-with-parsed-data
"Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2"
(mapcar #'butlast
@ -1944,14 +1957,14 @@ Para2"
info (org-element-map tree 'headline #'identity info t))))))
(should
(equal
'((1 "fn:1") (2 "fn:2"))
'((1 "1") (2 "2"))
(org-test-with-parsed-data
"Text[fn:1]\n* H\nText[fn:2]\n\n[fn:1] D1\n[fn:2] D2"
(mapcar #'butlast (org-export-collect-footnote-definitions info)))))
;; With optional argument BODY-FIRST, first check body, then
;; footnote definitions.
(should
(equal '("fn:1" "fn:3" "fn:2" nil)
(equal '("1" "3" "2" nil)
(org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
\[fn:2] B [fn:3] [fn::D].
@ -1960,7 +1973,7 @@ Para2"
(mapcar (lambda (e) (nth 1 e))
(org-export-collect-footnote-definitions info nil t)))))
(should-not
(equal '("fn:1" "fn:3" "fn:2" nil)
(equal '("1" "3" "2" nil)
(org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
\[fn:2] B [fn:3] [fn::D].
@ -1976,9 +1989,9 @@ Para2"
;; Read every type of footnote.
(should
(equal
'((1 . "A\n") (2 . "B") (3 . "C") (4 . "D"))
'((1 . "A\n") (2 . "C") (3 . "D"))
(org-test-with-parsed-data
"Text[fn:1] [1] [fn:label:C] [fn::D]\n\n[fn:1] A\n\n[1] B"
"Text[fn:1] [fn:label:C] [fn::D]\n\n[fn:1] A\n"
(org-element-map tree 'footnote-reference
(lambda (ref)
(let ((def (org-export-get-footnote-definition ref info)))
@ -1990,7 +2003,7 @@ Para2"
;; Test nested footnote in invisible definitions.
(should
(= 2
(org-test-with-temp-text "Text[1]\n\n[1] B [2]\n\n[2] C."
(org-test-with-temp-text "Text[fn:1]\n\n[fn:1] B [fn:2]\n\n[fn:2] C."
(narrow-to-region (point) (line-end-position))
(catch 'exit
(org-export-as
@ -2497,7 +2510,10 @@ Para2"
(org-export-resolve-fuzzy-link link info) info)) info t))))
;; Link to a target in a footnote should return footnote's number.
(org-test-with-parsed-data "
Paragraph[1][2][fn:lbl3:C<<target>>][[test]][[target]]\n[1] A\n\n[2] <<test>>B"
Paragraph[fn:1][fn:2][fn:lbl3:C<<target>>][[test]][[target]]
\[fn:1] A
\[fn:2] <<test>>B"
(should
(equal '(2 3)
(org-element-map tree 'link