org-footnote: Rename `org-footnote-goto-local-insertion-point'
* lisp/org-footnote.el (org-footnote--goto-local-insertion-point): Renamed from `org-footnote-goto-local-insertion-point'. (org-footnote-normalize): Use function above. Small refactoring. * testing/lisp/test-org-footnote.el (test-org-footnote/normalize-in-org): Small refactoring.
This commit is contained in:
parent
f6492d953c
commit
360c4633e2
|
@ -711,163 +711,162 @@ referenced sequence."
|
|||
(concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ ")))
|
||||
(count 0)
|
||||
ins-point ref ref-table)
|
||||
(save-excursion
|
||||
;; 1. Find every footnote reference, extract the definition, and
|
||||
;; collect that data in REF-TABLE. If SORT-ONLY is nil, also
|
||||
;; normalize references.
|
||||
(goto-char (point-min))
|
||||
(while (setq ref (org-footnote-get-next-reference))
|
||||
(let* ((lbl (car ref))
|
||||
(pos (nth 1 ref))
|
||||
;; When footnote isn't anonymous, check if it's label
|
||||
;; (REF) is already stored in REF-TABLE. In that case,
|
||||
;; extract number used to identify it (MARKER). If
|
||||
;; footnote is unknown, increment the global counter
|
||||
;; (COUNT) to create an unused identifier.
|
||||
(a (and lbl (assoc lbl ref-table)))
|
||||
(marker (or (nth 1 a) (incf count)))
|
||||
;; Is the reference inline or pointing to an inline
|
||||
;; footnote?
|
||||
(inlinep (or (stringp (nth 3 ref)) (nth 3 a))))
|
||||
;; Replace footnote reference with [MARKER]. Maybe fill
|
||||
;; paragraph once done. If SORT-ONLY is non-nil, only move
|
||||
;; to the end of reference found to avoid matching it twice.
|
||||
(if sort-only (goto-char (nth 2 ref))
|
||||
(delete-region (nth 1 ref) (nth 2 ref))
|
||||
(goto-char (nth 1 ref))
|
||||
(insert (format "[%d]" marker))
|
||||
(and inlinep
|
||||
org-footnote-fill-after-inline-note-extraction
|
||||
(org-fill-paragraph)))
|
||||
;; Add label (REF), identifier (MARKER), definition (DEF)
|
||||
;; type (INLINEP) and position (POS) to REF-TABLE if data
|
||||
;; was unknown.
|
||||
(unless a
|
||||
(let ((def (or (nth 3 ref) ; Inline definition.
|
||||
(nth 3 (org-footnote-get-definition lbl)))))
|
||||
(push (list lbl marker def
|
||||
;; 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
|
||||
((and org-footnote-section (derived-mode-p 'org-mode))
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward
|
||||
(concat "^\\*[ \t]+" (regexp-quote org-footnote-section)
|
||||
"[ \t]*$") nil t)
|
||||
(delete-region (match-beginning 0) (org-end-of-subtree t t)))
|
||||
;; A new footnote section is inserted by default at the end of
|
||||
;; the buffer.
|
||||
(goto-char (point-max))
|
||||
(skip-chars-backward " \r\t\n")
|
||||
(forward-line)
|
||||
(unless (bolp) (newline)))
|
||||
;; No footnote section set: Footnotes will be added at the end
|
||||
;; of the section containing their first reference.
|
||||
((derived-mode-p '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
|
||||
(let ((tag (concat "^" (regexp-quote
|
||||
org-footnote-tag-for-non-org-mode-files)
|
||||
"[ \t]*$")))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward tag nil t)
|
||||
(replace-match "")
|
||||
(delete-region (point) (progn (forward-line) (point))))))
|
||||
;; In Message mode, ensure footnotes are inserted before the
|
||||
;; signature.
|
||||
(if (and (derived-mode-p 'message-mode)
|
||||
(goto-char (point-max))
|
||||
(re-search-backward message-signature-separator nil t))
|
||||
(beginning-of-line)
|
||||
(goto-char (point-max)))))
|
||||
(setq ins-point (point-marker))
|
||||
;; 3. Clean-up REF-TABLE.
|
||||
(setq ref-table
|
||||
(delq nil
|
||||
(mapcar
|
||||
(lambda (x)
|
||||
(cond
|
||||
;; When only sorting, ignore inline footnotes.
|
||||
;; 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
|
||||
(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))))
|
||||
ref-table)
|
||||
;; 5. Insert the footnotes again in the buffer, at the
|
||||
;; appropriate spot.
|
||||
(goto-char ins-point)
|
||||
(cond
|
||||
;; No footnote: exit.
|
||||
((not ref-table))
|
||||
;; Cases when footnotes should be inserted in one place.
|
||||
((or (not (derived-mode-p 'org-mode)) org-footnote-section)
|
||||
;; Insert again the section title, if any. Ensure that title,
|
||||
;; or the subsequent footnotes, will be separated by a blank
|
||||
;; lines from the rest of the document. In an Org buffer,
|
||||
;; separate section with a blank line, unless explicitly
|
||||
;; stated in `org-blank-before-new-entry'.
|
||||
(if (not (derived-mode-p 'org-mode))
|
||||
(progn (skip-chars-backward " \t\n\r")
|
||||
(delete-region (point) ins-point)
|
||||
(unless (bolp) (newline))
|
||||
(when org-footnote-tag-for-non-org-mode-files
|
||||
(insert "\n" org-footnote-tag-for-non-org-mode-files "\n")))
|
||||
(when (and (cdr (assq 'heading org-blank-before-new-entry))
|
||||
(zerop (save-excursion (org-back-over-empty-lines))))
|
||||
(insert "\n"))
|
||||
(insert "* " org-footnote-section "\n"))
|
||||
(set-marker ins-point nil)
|
||||
;; Insert the footnotes, separated by a blank line.
|
||||
(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")))
|
||||
;; Each footnote definition has to be inserted at the end of
|
||||
;; the section where its first reference belongs.
|
||||
(t
|
||||
(mapc
|
||||
(org-with-wide-buffer
|
||||
;; 1. Find every footnote reference, extract the definition, and
|
||||
;; collect that data in REF-TABLE. If SORT-ONLY is nil, also
|
||||
;; normalize references.
|
||||
(goto-char (point-min))
|
||||
(while (setq ref (org-footnote-get-next-reference))
|
||||
(let* ((lbl (car ref))
|
||||
(pos (nth 1 ref))
|
||||
;; When footnote isn't anonymous, check if it's label
|
||||
;; (REF) is already stored in REF-TABLE. In that case,
|
||||
;; extract number used to identify it (MARKER). If
|
||||
;; footnote is unknown, increment the global counter
|
||||
;; (COUNT) to create an unused identifier.
|
||||
(a (and lbl (assoc lbl ref-table)))
|
||||
(marker (or (nth 1 a) (incf count)))
|
||||
;; Is the reference inline or pointing to an inline
|
||||
;; footnote?
|
||||
(inlinep (or (stringp (nth 3 ref)) (nth 3 a))))
|
||||
;; Replace footnote reference with [MARKER]. Maybe fill
|
||||
;; paragraph once done. If SORT-ONLY is non-nil, only move
|
||||
;; to the end of reference found to avoid matching it twice.
|
||||
(if sort-only (goto-char (nth 2 ref))
|
||||
(delete-region (nth 1 ref) (nth 2 ref))
|
||||
(goto-char (nth 1 ref))
|
||||
(insert (format "[%d]" marker))
|
||||
(and inlinep
|
||||
org-footnote-fill-after-inline-note-extraction
|
||||
(org-fill-paragraph)))
|
||||
;; Add label (REF), identifier (MARKER), definition (DEF)
|
||||
;; type (INLINEP) and position (POS) to REF-TABLE if data was
|
||||
;; unknown.
|
||||
(unless a
|
||||
(let ((def (or (nth 3 ref) ; Inline definition.
|
||||
(nth 3 (org-footnote-get-definition lbl)))))
|
||||
(push (list lbl marker def
|
||||
;; 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
|
||||
((and org-footnote-section (derived-mode-p 'org-mode))
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward
|
||||
(concat "^\\*[ \t]+" (regexp-quote org-footnote-section)
|
||||
"[ \t]*$") nil t)
|
||||
(delete-region (match-beginning 0) (org-end-of-subtree t t)))
|
||||
;; A new footnote section is inserted by default at the end of
|
||||
;; the buffer.
|
||||
(goto-char (point-max))
|
||||
(skip-chars-backward " \r\t\n")
|
||||
(forward-line)
|
||||
(unless (bolp) (newline)))
|
||||
;; No footnote section set: Footnotes will be added at the end
|
||||
;; of the section containing their first reference.
|
||||
((derived-mode-p '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
|
||||
(let ((tag (concat "^" (regexp-quote
|
||||
org-footnote-tag-for-non-org-mode-files)
|
||||
"[ \t]*$")))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward tag nil t)
|
||||
(replace-match "")
|
||||
(delete-region (point) (progn (forward-line) (point))))))
|
||||
;; In Message mode, ensure footnotes are inserted before the
|
||||
;; signature.
|
||||
(if (and (derived-mode-p 'message-mode)
|
||||
(goto-char (point-max))
|
||||
(re-search-backward message-signature-separator nil t))
|
||||
(beginning-of-line)
|
||||
(goto-char (point-max)))))
|
||||
(setq ins-point (point-marker))
|
||||
;; 3. Clean-up REF-TABLE.
|
||||
(setq ref-table
|
||||
(delq nil
|
||||
(mapcar
|
||||
(lambda (x)
|
||||
(cond
|
||||
;; When only sorting, ignore inline footnotes.
|
||||
;; 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
|
||||
(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.
|
||||
(dolist (x ref-table)
|
||||
(unless (nth 3 x) (org-footnote-delete-definitions (car x))))
|
||||
;; 5. Insert the footnotes again in the buffer, at the
|
||||
;; appropriate spot.
|
||||
(goto-char ins-point)
|
||||
(cond
|
||||
;; No footnote: exit.
|
||||
((not ref-table))
|
||||
;; Cases when footnotes should be inserted in one place.
|
||||
((or (not (derived-mode-p 'org-mode)) org-footnote-section)
|
||||
;; Insert again the section title, if any. Ensure that title,
|
||||
;; or the subsequent footnotes, will be separated by a blank
|
||||
;; lines from the rest of the document. In an Org buffer,
|
||||
;; separate section with a blank line, unless explicitly stated
|
||||
;; in `org-blank-before-new-entry'.
|
||||
(if (not (derived-mode-p 'org-mode))
|
||||
(progn (skip-chars-backward " \t\n\r")
|
||||
(delete-region (point) ins-point)
|
||||
(unless (bolp) (newline))
|
||||
(when org-footnote-tag-for-non-org-mode-files
|
||||
(insert "\n" org-footnote-tag-for-non-org-mode-files "\n")))
|
||||
(when (and (cdr (assq 'heading org-blank-before-new-entry))
|
||||
(zerop (save-excursion (org-back-over-empty-lines))))
|
||||
(insert "\n"))
|
||||
(insert "* " org-footnote-section "\n"))
|
||||
(set-marker ins-point nil)
|
||||
;; Insert the footnotes, separated by a blank line.
|
||||
(insert
|
||||
(mapconcat
|
||||
(lambda (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"
|
||||
(if sort-only (car x) (nth 1 x))
|
||||
(nth 2 x))))
|
||||
ref-table))))))
|
||||
;; 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")))
|
||||
;; Each footnote definition has to be inserted at the end of the
|
||||
;; section where its first reference belongs.
|
||||
(t
|
||||
(dolist (x ref-table)
|
||||
(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 (if sort-only 0 1) x)
|
||||
(nth 2 x)))))))))
|
||||
|
||||
(defun org-footnote-goto-local-insertion-point ()
|
||||
"Find insertion point for footnote, just before next outline heading."
|
||||
(defun org-footnote--goto-local-insertion-point ()
|
||||
"Find insertion point for footnote, just before next outline heading.
|
||||
Assume insertion point is within currently accessible part of the buffer."
|
||||
(org-with-limited-levels (outline-next-heading))
|
||||
(or (bolp) (newline))
|
||||
(beginning-of-line 0)
|
||||
(while (and (not (bobp)) (= (char-after) ?#))
|
||||
(beginning-of-line 0))
|
||||
(if (let ((case-fold-search t)) (looking-at "[ \t]*#\\+tblfm:")) (beginning-of-line 2))
|
||||
(end-of-line 1)
|
||||
(skip-chars-backward "\n\r\t ")
|
||||
(forward-line))
|
||||
;; Skip file local variables. See `modify-file-local-variable'.
|
||||
(when (eobp)
|
||||
(let ((case-fold-search t))
|
||||
(re-search-backward "^[ \t]*# +Local Variables:"
|
||||
(max (- (point-max) 3000) (point-min))
|
||||
t)))
|
||||
(skip-chars-backward " \t\n")
|
||||
(forward-line)
|
||||
(unless (bolp) (insert "\n")))
|
||||
|
||||
(defun org-footnote-delete-references (label)
|
||||
"Delete every reference to footnote LABEL.
|
||||
|
|
|
@ -185,25 +185,10 @@
|
|||
|
||||
(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]
|
||||
;; 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
|
||||
|
||||
|
@ -218,51 +203,62 @@
|
|||
\[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]
|
||||
"
|
||||
(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] Definition 1
|
||||
* Head2"
|
||||
(org-footnote-normalize)
|
||||
(should
|
||||
(equal (buffer-string)
|
||||
"* Head1
|
||||
|
||||
\[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"))))
|
||||
;; 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] 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
|
||||
|
||||
|
@ -273,20 +269,22 @@ Body[1]
|
|||
\[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
|
||||
"
|
||||
(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
|
||||
;; containing its first reference.
|
||||
(should
|
||||
(equal "* Head 1
|
||||
Text[1]
|
||||
|
||||
\[1] Def1
|
||||
|
@ -296,7 +294,17 @@ Text[1]
|
|||
Text[2]
|
||||
|
||||
\[2] Def2
|
||||
")))))
|
||||
"
|
||||
(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))))))
|
||||
|
||||
(ert-deftest test-org-footnote/normalize-outside-org ()
|
||||
"Test `org-footnote-normalize' specifications for buffers not in Org mode."
|
||||
|
|
Loading…
Reference in New Issue