From ca060f7be76814d603bf5d17bd55e4131e3a0bdb Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Thu, 21 Feb 2013 15:30:16 +0100 Subject: [PATCH] Require 2 blank lines to separate footnote definition * lisp/org-element.el (org-element-footnote-definition-parser): Require 2 blank lines to separate footnote definition. * lisp/org-footnote.el (org-footnote-at-definition-p): Require 2 blank lines to separate footnote definition. * doc/org.texi: Update documentation for footnotes. * testing/lisp/test-org-element.el: Update tests. * testing/lisp/test-org-footnote.el: Add tests. Footnote definitions can still be separated with other footnote definitions and headlines. This change allows to have multiple paragraphs in a footnote definition without resorting to the "\par" trick. --- doc/org.texi | 14 ++--- lisp/org-element.el | 2 +- lisp/org-footnote.el | 13 ++--- testing/lisp/test-org-element.el | 4 +- testing/lisp/test-org-footnote.el | 87 +++++++++++++++++++++++++------ 5 files changed, 89 insertions(+), 31 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index f9df81996..0758dee1a 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -1845,13 +1845,13 @@ or on a per-file basis by using @cindex footnotes Org mode supports the creation of footnotes. In contrast to the -@file{footnote.el} package, Org mode's footnotes are designed for work on a -larger document, not only for one-off documents like emails. The basic -syntax is similar to the one used by @file{footnote.el}, i.e., a footnote is -defined in a paragraph that is started by a footnote marker in square -brackets in column 0, no indentation allowed. If you need a paragraph break -inside a footnote, use the @LaTeX{} idiom @samp{\par}. The footnote reference -is simply the marker in square brackets, inside text. For example: +@file{footnote.el} package, Org mode's footnotes are designed for work on +a larger document, not only for one-off documents like emails. + +A footnote is started by a footnote marker in square brackets in column 0, no +indentation allowed. It ends at the next footnote definition, headline, or +after two consecutive empty lines. The footnote reference is simply the +marker in square brackets, inside text. For example: @example The Org homepage[fn:1] now looks a lot better than it used to. diff --git a/lisp/org-element.el b/lisp/org-element.el index fc2f5dd28..d09147d83 100644 --- a/lisp/org-element.el +++ b/lisp/org-element.el @@ -693,7 +693,7 @@ Assume point is at the beginning of the footnote definition." (re-search-forward (concat org-outline-regexp-bol "\\|" org-footnote-definition-re "\\|" - "^[ \t]*$") limit 'move)) + "^\\([ \t]*\n\\)\\{2,\\}") limit 'move)) (match-beginning 0) (point)))) (contents-begin (progn (search-forward "]") diff --git a/lisp/org-footnote.el b/lisp/org-footnote.el index 9aa388b79..02af43f50 100644 --- a/lisp/org-footnote.el +++ b/lisp/org-footnote.el @@ -251,11 +251,12 @@ otherwise." (when (save-excursion (beginning-of-line) (org-footnote-in-valid-context-p)) (save-excursion (end-of-line) - ;; Footnotes definitions are separated by new headlines or blank - ;; lines. - (let ((lim (save-excursion (re-search-backward - (concat org-outline-regexp-bol - "\\|^[ \t]*$") nil t)))) + ;; Footnotes definitions are separated by new headlines, another + ;; footnote definition or 2 blank lines. + (let ((lim (save-excursion + (re-search-backward + (concat org-outline-regexp-bol + "\\|^\\([ \t]*\n\\)\\{2,\\}") nil t)))) (when (re-search-backward org-footnote-definition-re lim t) (let ((label (org-match-string-no-properties 1)) (beg (match-beginning 0)) @@ -271,7 +272,7 @@ otherwise." (re-search-forward (concat org-outline-regexp-bol "\\|" org-footnote-definition-re "\\|" - "^[ \t]*$") bound 'move)) + "^\\([ \t]*\n\\)\\{2,\\}") bound 'move)) (match-beginning 0) (point))))) (list label beg end diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index a65355f86..c979715e6 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -724,10 +724,10 @@ Some other text (org-element-parse-buffer) 'footnote-definition 'identity nil t))) ;; Footnote with more contents (should - (= 28 + (= 29 (org-element-property :end - (org-test-with-temp-text "[fn:1] Definition\n| a | b |" + (org-test-with-temp-text "[fn:1] Definition\n\n| a | b |" (org-element-map (org-element-parse-buffer) 'footnote-definition 'identity nil t))))) diff --git a/testing/lisp/test-org-footnote.el b/testing/lisp/test-org-footnote.el index f55ed843b..5fb7351b7 100644 --- a/testing/lisp/test-org-footnote.el +++ b/testing/lisp/test-org-footnote.el @@ -19,6 +19,58 @@ ;;; Code: +(ert-deftest test-org-footnote/delete () + "Test `org-footnote-delete' specifications." + ;; Regular test. + (should + (equal "Paragraph" + (org-test-with-temp-text "Paragraph[1]\n\n[1] Definition" + (search-forward "[") + (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 "[") + (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-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))))) + ;; With an argument, delete footnote with specified label. + (should + (equal "Paragraph[1] and another\n\n[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" + (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-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)))))) + (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'. @@ -138,21 +190,10 @@ Text[2] "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] + (should + (equal + "Paragraph[1][2][3][4][5] -\[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. @@ -164,7 +205,21 @@ Some additional text. \[4] Inline -\[5] Anonymous")))) +\[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:1] Standard + +\[fn:label] Labelled + +\[1] Numbered + + +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 @@ -174,12 +229,14 @@ Some additional text. \[fn:1] Standard + Some additional text.") (org-footnote-normalize) (should (equal (buffer-string) "Paragraph[1][2] + Some additional text. Footnotes: