From 2b2c603903ea59e42d5b469eacd9deae3d56d948 Mon Sep 17 00:00:00 2001 From: Carsten Dominik Date: Sat, 3 Jan 2009 09:03:04 +0100 Subject: [PATCH] Footnotes: When sorting footnotes, respect the location settings. Sorting footnotes used to be almost like normalization, in that all footnotes would be collected into a single location. Now sorting respects the setting of `org-footnote-section'. If that is nil, sorting will actually move each footnote into the outline node of its first reference. --- doc/org.texi | 23 +++++---- lisp/org-footnote.el | 108 ++++++++++++++++++++++++++++++++----------- 2 files changed, 95 insertions(+), 36 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index 39469f11e..e25292e78 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -1415,20 +1415,25 @@ for details. @table @kbd @kindex C-c C-x f @item C-c C-x f -The footnote action command. When the cursor is on a footnote reference, -jump to the definition. When it is at a definition, jump to the (first) -reference. Otherwise, create a new footnote. Depending on the variable +The footnote action command. + +When the cursor is on a footnote reference, jump to the definition. When it +is at a definition, jump to the (first) reference. + +Otherwise, create a new footnote. Depending on the variable @code{org-footnote-define-inline}@footnote{The corresponding in-buffer setting is: @code{#+STARTUP: fninline} or @code{#+STARTUP: nofninline}}, the -definitions will be placed locally, or into the nearest outline section with -the heading @samp{Footnotes}. If no such section is found after the -reference point, one will be created at the end of the file.@* When this -command is called with a prefix argument, a menu of additional options is -offered: +definition will be placed right into the text as part of the reference, or +separately into the location determined by the variable +@code{org-footnote-section}. + +When this command is called with a prefix argument, a menu of additional +options is offered: @example s @r{Sort the footnote definitions by reference sequence. During editing,} @r{Org makes no effort to sort footnote definitions into a particular} - @r{sequence. If you want them sorted, use this command.} + @r{sequence. If you want them sorted, use this command, which will} + @r{also move entries according to @code{org-footnote-section}.} n @r{Normalize the footnotes by collecting all definitions (including} @r{inline definitions) into a special section, and then numbering them} @r{in sequence. The references will then also be numbers. This is} diff --git a/lisp/org-footnote.el b/lisp/org-footnote.el index 6bf6bf665..af5467c61 100644 --- a/lisp/org-footnote.el +++ b/lisp/org-footnote.el @@ -42,6 +42,7 @@ (declare-function org-mark-ring-push "org" (&optional pos buffer)) (declare-function outline-next-heading "outline") (declare-function org-trim "org" (s)) +(declare-function org-show-context "org" (&optional key)) (declare-function org-back-to-heading "org" (&optional invisible-ok)) (declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading)) @@ -66,13 +67,14 @@ This can be nil, to place footnotes locally at the end of the current outline node. If can also be the name of a special outline heading under which footnotes should be put. This variable defines the place where Org puts the definition -automatically. However, by hand you may place definitions *anywhere*. +automatically, i.e. when creating the footnote, and when sorting the notes. +However, by hand you may place definitions *anywhere*. If this is a string, during export, all subtrees starting with this heading will be removed after extracting footnote definitions." :group 'org-footnotes :type '(choice - (string :tag "Special outline node name") - (const :tag "Define footnotes in the current outline node" nil))) + (string :tag "Collect fotnotes under heading") + (const :tag "Define footnotes locally" nil))) (defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:" "Tag marking the beginning of footnote section. @@ -109,6 +111,14 @@ plain Automatically create plain number labels like [1]" (const :tag "Offer automatic [fn:N] for editing" confirm) (const :tag "Create automatic [N]" plain))) +(defcustom org-footnote-fill-after-inline-note-extraction nil + "Non-nil means, fill paragraphs after extracting footnotes. +When extracting inline footnotes, the lengths of lines can change a lot. +When this option is set, paragraphs from which an inline footnote has been +extracted will be filled again." + :group 'org-footnote + :type 'boolean) + (defun org-footnote-at-reference-p () "Is the cursor at a footnote reference? If yes, return the beginning position, the label, and the definition, if local." @@ -249,7 +259,7 @@ or new, let the user edit the definition of the footnote." (cond ((org-mode-p) (if (not org-footnote-section) - ;; No section, put foornote into the curren outline node + ;; No section, put footnote into the current outline node nil ;; Try to find or make the special node (setq re (concat "^\\*+[ \t]+" org-footnote-section "[ \t]*$")) @@ -259,10 +269,7 @@ or new, let the user edit the definition of the footnote." (goto-char (point-max)) (insert "\n\n* " org-footnote-section))) ;; Now go to the end of this entry and insert there. - (outline-next-heading) - (setq p (point)) - (skip-chars-backward " \t\n\r") - (delete-region (point) p)) + (org-footnote-goto-local-insertion-point)) (t (setq re (concat "^" org-footnote-tag-for-non-org-mode-files "[ \t]*$")) (unless (re-search-forward re nil t) @@ -272,10 +279,8 @@ or new, let the user edit the definition of the footnote." (delete-region (point) (point-max)) (insert org-footnote-tag-for-non-org-mode-files "\n")) (goto-char (point-max)) - (skip-chars-backward " \t\r\n") - (delete-region (point) (point-max)))) - (insert "\n\n\n") - (backward-char 1) + (skip-chars-backward " \t\r\n"))) + (insert "\n\n") (insert "[" label "] ") (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'."))) @@ -317,19 +322,22 @@ Org-mode exporters. When SORT-ONLY is set, only sort the footnote definitions into the referenced sequence." ;; This is based on Paul's function, but rewritten. - (let ((count 0) ref def ref-table liste beg beg1 ref def marker a before + (let ((count 0) ref def idef ref-table liste beg beg1 marker a before ins-point) (save-excursion - ;; Now find footnote references, + ;; Now find footnote references, and extract the definitions (goto-char (point-min)) (while (re-search-forward org-footnote-re nil t) (org-if-unprotected (setq def (match-string 4) + idef def ref (or (match-string 1) (match-string 2)) before (char-to-string (char-after (match-beginning 0)))) (if (equal ref "fn:") (setq ref nil)) (if (and ref (setq a (assoc ref ref-table))) - (setq marker (nth 1 a)) + (progn + (setq marker (nth 1 a)) + (unless (nth 2 a) (setf (caddr a) def))) (setq marker (number-to-string (incf count)))) (save-match-data (if def @@ -337,8 +345,7 @@ referenced sequence." (save-excursion (if (not (re-search-forward (concat "^\\[" (regexp-quote ref) "\\]") nil t)) - (setq def - (format "FOOTNOTE DEFINITION NOT FOUND: %s" ref)) + (setq def nil) (setq beg (match-beginning 0)) (setq beg1 (match-end 0)) (re-search-forward @@ -346,7 +353,11 @@ referenced sequence." nil 'move) (setq def (buffer-substring beg1 (match-beginning 0))) (delete-region beg (match-beginning 0)))))) - (unless sort-only (replace-match (concat before "[" marker "]"))) + (unless sort-only + (replace-match (concat before "[" marker "]")) + (and idef + org-footnote-fill-after-inline-note-extraction + (fill-paragraph))) (if (not a) (push (list ref marker def) ref-table)))) ;; First find and remove the footnote section @@ -358,7 +369,7 @@ referenced sequence." (concat "^\\*[ \t]+" (regexp-quote org-footnote-section) "[ \t]*$") nil t)) - (if for-preprocessor + (if (or for-preprocessor (not org-footnote-section)) (replace-match "") (org-back-to-heading t) (forward-line 1) @@ -381,24 +392,67 @@ referenced sequence." (delete-region (point) (point-max)) (insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n") (setq ins-point (point)))) - + ;; Insert the footnotes again (goto-char (or ins-point (point-max))) (setq ref-table (reverse ref-table)) (when sort-only + ;; remove anonymous fotnotes from the list (setq ref-table (delq nil (mapcar (lambda (x) (and (car x) (not (equal (car x) "fn:")) x)) ref-table)))) - (setq def - (mapconcat + ;; Make sure each footnote has a description, or an error message. + (setq ref-table + (mapcar (lambda (x) - (format "[%s] %s" (nth (if sort-only 0 1) x) - (org-trim (nth 2 x)))) - ref-table "\n\n")) - (if ref-table (insert "\n" def "\n\n"))))) + (if (not (nth 2 x)) + (setcar (cddr x) + (format "FOOTNOTE DEFINITION NOT FOUND: %s" (car x))) + (setcar (cddr x) (org-trim (nth 2 x)))) + x) + ref-table)) + + (if (or (not (org-mode-p)) ; not an Org file + org-footnote-section ; we do not use a footnote section + (not sort-only) ; this is normalization + for-preprocessor) ; the is the preprocessor + ;; Insert the footnotes together in one place + (progn + (setq def + (mapconcat + (lambda (x) + (format "[%s] %s" (nth (if sort-only 0 1) x) + (org-trim (nth 2 x)))) + ref-table "\n\n")) + (if ref-table (insert "\n" def "\n\n"))) + ;; Insert each footnote near the first reference + ;; Happens only in Org files with no special footnote section, + ;; and only when doing sorting + (mapc 'org-insert-footnote-reference-near-definition + ref-table))))) + +(defun org-insert-footnote-reference-near-definition (entry) + "Find first reference of footnote ENTRY and insert the definition there. +ENTRY is (fn-label num-mark definition)." + (when (car entry) + (let ((pos (point))) + (goto-char (point-min)) + (when (re-search-forward (format ".\\[%s[]:]" (regexp-quote (car entry))) + nil t) + (org-footnote-goto-local-insertion-point) + (insert (format "\n\n[%s] %s" (car entry) (nth 2 entry))))))) + +(defun org-footnote-goto-local-insertion-point () + "Find insertion point for footnote, just before next outline heading." + (outline-next-heading) + (beginning-of-line 0) + (while (and (not (bobp)) (= (char-after) ?#)) + (beginning-of-line 0)) + (if (looking-at "#\\+TBLFM:") (beginning-of-line 2)) + (skip-chars-backward "\n\r\t ")) (defun org-footnote-delete (&optional label) "Delete the footnote at point. @@ -439,4 +493,4 @@ and all references of a footnote label." (provide 'org-footnote) ;; arch-tag: 1b5954df-fb5d-4da5-8709-78d944dbfc37 -;;; org-footnote.el ends here \ No newline at end of file +;;; org-footnote.el ends here