X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/65c439fd68997aa8f44cad817d09f97c35b0635c..322b7dab59b98b5d8625d2cd29e48f1ce605f769:/lisp/org/org-footnote.el diff --git a/lisp/org/org-footnote.el b/lisp/org/org-footnote.el index 4372411c06..c0ca570b7b 100644 --- a/lisp/org/org-footnote.el +++ b/lisp/org/org-footnote.el @@ -1,11 +1,11 @@ ;;; org-footnote.el --- Footnote support in Org and elsewhere ;; -;; Copyright (C) 2009 Free Software Foundation, Inc. +;; Copyright (C) 2009-2011 Free Software Foundation, Inc. ;; ;; Author: Carsten Dominik ;; Keywords: outlines, hypermedia, calendar, wp ;; Homepage: http://orgmode.org -;; Version: 6.19e +;; Version: 7.4 ;; ;; This file is part of GNU Emacs. ;; @@ -38,6 +38,7 @@ (require 'org-macs) (require 'org-compat) +(declare-function org-in-commented-line "org" ()) (declare-function org-in-regexp "org" (re &optional nlines visually)) (declare-function org-mark-ring-push "org" (&optional pos buffer)) (declare-function outline-next-heading "outline") @@ -45,6 +46,10 @@ (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)) +(declare-function org-in-verbatim-emphasis "org" ()) +(declare-function org-inside-latex-macro-p "org" ()) +(defvar org-odd-levels-only) ;; defined in org.el +(defvar message-signature-separator) ;; defined in message.el (defconst org-footnote-re (concat "[^][\n]" ; to make sure it is not at the beginning of a line @@ -57,10 +62,15 @@ "\\]") "Regular expression for matching footnotes.") -(defconst org-footnote-definition-re +(defconst org-footnote-definition-re (org-re "^\\(\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]\\)") "Regular expression matching the definition of a footnote.") +(defgroup org-footnote nil + "Footnotes in Org-mode." + :tag "Org Footnote" + :group 'org) + (defcustom org-footnote-section "Footnotes" "Outline heading containing footnote definitions before export. This can be nil, to place footnotes locally at the end of the current @@ -71,9 +81,9 @@ 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 + :group 'org-footnote :type '(choice - (string :tag "Collect fotnotes under heading") + (string :tag "Collect footnotes under heading") (const :tag "Define footnotes locally" nil))) (defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:" @@ -83,11 +93,11 @@ as in Org-mode. Outside Org-mode, new footnotes are always placed at the end of the file. When you normalize the notes, any line containing only this tag will be removed, a new one will be inserted at the end of the file, followed by the collected and normalized footnotes." - :group 'org-footnotes + :group 'org-footnote :type 'string) (defcustom org-footnote-define-inline nil - "Non-nil means, define footnotes inline, at reference location. + "Non-nil means define footnotes inline, at reference location. When nil, footnotes will be defined in a special section near the end of the document. When t, the [fn:label:definition] notation will be used to define the footnote at the reference position." @@ -95,7 +105,7 @@ will be used to define the footnote at the reference position." :type 'boolean) (defcustom org-footnote-auto-label t - "Non-nil means, define automatically new labels for footnotes. + "Non-nil means define automatically new labels for footnotes. Possible values are: nil prompt the user for each label @@ -106,13 +116,30 @@ confirm like t, but let the user edit the created value. In particular, plain Automatically create plain number labels like [1]" :group 'org-footnote :type '(choice - (const :tag "Frompt for label" nil) + (const :tag "Prompt for label" nil) (const :tag "Create automatic [fn:N]" t) (const :tag "Offer automatic [fn:N] for editing" confirm) (const :tag "Create automatic [N]" plain))) +(defcustom org-footnote-auto-adjust nil + "Non-nil means automatically adjust footnotes after insert/delete. +When this is t, after each insertion or deletion of a footnote, +simple fn:N footnotes will be renumbered, and all footnotes will be sorted. +If you want to have just sorting or just renumbering, set this variable +to `sort' or `renumber'. + +The main values of this variable can be set with in-buffer options: + +#+STARTUP: fnadjust +#+STARTUP: nofnadjust" + :group 'org-footnote + :type '(choice + (const :tag "Renumber" renumber) + (const :tag "Sort" sort) + (const :tag "Renumber and Sort" t))) + (defcustom org-footnote-fill-after-inline-note-extraction nil - "Non-nil means, fill paragraphs after extracting footnotes. + "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." @@ -124,7 +151,7 @@ extracted will be filled again." If yes, return the beginning position, the label, and the definition, if local." (when (org-in-regexp org-footnote-re 15) (list (match-beginning 0) - (or (match-string 1) + (or (match-string 1) (if (equal (match-string 2) "fn:") nil (match-string 2))) (match-string 4)))) @@ -133,7 +160,7 @@ If yes, return the beginning position, the label, and the definition, if local." This matches only pure definitions like [1] or [fn:name] at the beginning of a line. It does not a references like [fn:name:definition], where the footnote text is included and defined locally. -The return value will be nil if not at a foornote definition, and a list +The return value will be nil if not at a footnote definition, and a list with start and label of the footnote if there is a definition at point." (save-excursion (end-of-line 1) @@ -161,25 +188,25 @@ with start and label of the footnote if there is a definition at point." (org-show-context 'link-search) (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")))) -(defun org-footnote-goto-next-reference (label) - "Find the definition of the footnote with label LABEL." +(defun org-footnote-goto-previous-reference (label) + "Find the first closest (to point) reference of footnote with label LABEL." (interactive "sLabel: ") (org-mark-ring-push) (setq label (org-footnote-normalize-label label)) (let ((re (format ".\\[%s[]:]" label)) (p0 (point)) pos) (save-excursion - (setq pos (or (re-search-forward re nil t) - (and (goto-char (point-min)) - (re-search-forward re nil t)) + (setq pos (or (re-search-backward re nil t) + (and (goto-char (point-max)) + (re-search-backward re nil t)) (and (progn (widen) t) (goto-char p0) - (re-search-forward re nil t)) - (and (goto-char (point-min)) + (re-search-backward re nil t)) + (and (goto-char (point-max)) (re-search-forward re nil t))))) (if pos (progn - (goto-char pos) + (goto-char (match-end 0)) (org-show-context 'link-search)) (error "Cannot find reference of footnote %s" label)))) @@ -246,10 +273,12 @@ or new, let the user edit the definition of the footnote." (message "New reference to existing note")) (org-footnote-define-inline (insert "[" label ": ]") - (backward-char 1)) + (backward-char 1) + (org-footnote-auto-adjust-maybe)) (t (insert "[" label "]") - (org-footnote-create-definition label))))) + (org-footnote-create-definition label) + (org-footnote-auto-adjust-maybe))))) (defun org-footnote-create-definition (label) "Start the definition of a footnote with label LABEL." @@ -269,37 +298,47 @@ or new, let the user edit the definition of the footnote." (goto-char (point-max)) (insert "\n\n* " org-footnote-section "\n"))) ;; Now go to the end of this entry and insert there. - (org-footnote-goto-local-insertion-point)) + (org-footnote-goto-local-insertion-point) + (org-show-context 'link-search)) (t (setq re (concat "^" org-footnote-tag-for-non-org-mode-files "[ \t]*$")) (unless (re-search-forward re nil t) - (goto-char (point-max)) - (skip-chars-backward " \t\r\n") - (insert "\n\n") - (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"))) - (insert "\n\n") - (insert "[" label "] ") + (let ((max (if (and (derived-mode-p 'message-mode) + (re-search-forward message-signature-separator nil t)) + (progn (beginning-of-line) (point)) + (goto-char (point-max))))) + (skip-chars-backward " \t\r\n") + (delete-region (point) max) + (insert "\n\n") + (insert org-footnote-tag-for-non-org-mode-files "\n"))))) + ;; Skip existing footnotes + (while (re-search-forward "^[[:space:]]*\\[[^]]+\\] " nil t) + (forward-line)) + (insert "[" label "] \n") + (goto-char (1- (point))) (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'."))) ;;;###autoload (defun org-footnote-action (&optional special) "Do the right thing for footnotes. -When at a foornote reference, jump to the definition. When at a definition, -jump to the refernces. When neither at definition or reference, +When at a footnote reference, jump to the definition. When at a definition, +jump to the references. When neither at definition or reference, create a new footnote, interactively. With prefix arg SPECIAL, offer additional commands in a menu." (interactive "P") (let (tmp c) (cond (special - (message "Footnotes: [s]ort | convert to [n]umeric | [d]elete") + (message "Footnotes: [s]ort | [r]enumber fn:N | [S]=r+s |->[n]umeric | [d]elete") (setq c (read-char-exclusive)) (cond ((equal c ?s) (org-footnote-normalize 'sort)) + ((equal c ?r) + (org-footnote-renumber-fn:N)) + ((equal c ?S) + (org-footnote-renumber-fn:N) + (org-footnote-normalize 'sort)) ((equal c ?n) (org-footnote-normalize)) ((equal c ?d) @@ -310,60 +349,73 @@ With prefix arg SPECIAL, offer additional commands in a menu." (org-footnote-goto-definition (nth 1 tmp)) (goto-char (match-beginning 4)))) ((setq tmp (org-footnote-at-definition-p)) - (org-footnote-goto-next-reference (nth 1 tmp))) + (org-footnote-goto-previous-reference (nth 1 tmp))) (t (org-footnote-new))))) ;;;###autoload (defun org-footnote-normalize (&optional sort-only for-preprocessor) "Collect the footnotes in various formats and normalize them. -This find the different sorts of footnotes allowed in Org, and +This finds the different sorts of footnotes allowed in Org, and normalizes them to the usual [N] format that is understood by the 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 idef ref-table beg beg1 marker a before - ins-point) + (let* ((limit-level + (and (boundp 'org-inlinetask-min-level) + org-inlinetask-min-level + (1- org-inlinetask-min-level))) + (nstars (and limit-level + (if org-odd-levels-only + (and limit-level (1- (* limit-level 2))) + limit-level))) + (outline-regexp + (concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ "))) + (count 0) + ref def idef ref-table beg beg1 marker a before ins-point) (save-excursion ;; 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))) - (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 - (setq def (org-trim def)) - (save-excursion - (goto-char (point-min)) - (if (not (re-search-forward (concat "^\\[" (regexp-quote ref) - "\\]") nil t)) - (setq def nil) - (setq beg (match-beginning 0)) - (setq beg1 (match-end 0)) - (re-search-forward - (org-re "^[ \t]*$\\|^\\*+ \\|^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]") - nil 'move) - (setq def (buffer-substring beg1 (or (match-beginning 0) - (point-max)))) - (goto-char beg) - (skip-chars-backward " \t\n\t") - (delete-region (1+ (point)) (match-beginning 0)))))) - (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)))) - + (unless (or (org-in-commented-line) (org-in-verbatim-emphasis) + (org-inside-latex-macro-p)) + (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))) + (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 + (setq def (org-trim def)) + (save-excursion + (goto-char (point-min)) + (if (not (re-search-forward (concat "^\\[" (regexp-quote ref) + "\\]") nil t)) + (setq def nil) + (setq beg (match-beginning 0)) + (setq beg1 (match-end 0)) + (re-search-forward + (org-re "^[ \t]*$\\|^\\*+ \\|^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]") + nil 'move) + (setq def (buffer-substring beg1 (or (match-beginning 0) + (point-max)))) + (goto-char beg) + (skip-chars-backward " \t\n\t") + (delete-region (1+ (point)) (match-beginning 0)))))) + (unless sort-only + (replace-match (concat before "[" marker "]") t t) + (and idef + org-footnote-fill-after-inline-note-extraction + (fill-paragraph))) + (if (not a) (push (list ref marker def (if idef t nil)) + ref-table))))) + ;; First find and remove the footnote section (goto-char (point-min)) (cond @@ -386,7 +438,7 @@ referenced sequence." (insert "* " org-footnote-section "\n") (setq ins-point (point)))))) (t - (if (re-search-forward + (if (re-search-forward (concat "^" (regexp-quote org-footnote-tag-for-non-org-mode-files) "[ \t]*$") @@ -397,16 +449,17 @@ 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 + ;; remove anonymous and inline footnotes from the list (setq ref-table (delq nil (mapcar (lambda (x) (and (car x) (not (equal (car x) "fn:")) + (not (nth 3 x)) x)) ref-table)))) ;; Make sure each footnote has a description, or an error message. @@ -451,14 +504,15 @@ ENTRY is (fn-label num-mark definition)." (defun org-footnote-goto-local-insertion-point () "Find insertion point for footnote, just before next outline heading." - (outline-next-heading) + (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 (looking-at "#\\+TBLFM:") (beginning-of-line 2)) + (if (looking-at "[ \t]*#\\+TBLFM:") (beginning-of-line 2)) (end-of-line 1) - (skip-chars-backward "\n\r\t ")) + (skip-chars-backward "\n\r\t ") + (forward-line)) (defun org-footnote-delete (&optional label) "Delete the footnote at point. @@ -493,11 +547,43 @@ and all references of a footnote label." (goto-char (point-max))) (delete-region beg (point)) (incf ndef)))) + (org-footnote-auto-adjust-maybe) (message "%d definition(s) of and %d reference(s) of footnote %s removed" ndef nref label)))) +(defun org-footnote-renumber-fn:N () + "Renumber the simple footnotes like fn:17 into a sequence in the document." + (interactive) + (let (map i (n 0)) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (while (re-search-forward "\\[fn:\\([0-9]+\\)[]:]" nil t) + (setq i (string-to-number (match-string 1))) + (when (and (string-match "\\S-" (buffer-substring + (point-at-bol) (match-beginning 0))) + (not (assq i map))) + (push (cons i (number-to-string (incf n))) map))) + (goto-char (point-min)) + (while (re-search-forward "\\(\\[fn:\\)\\([0-9]+\\)\\([]:]\\)" nil t) + (replace-match (concat "\\1" (cdr (assq (string-to-number (match-string 2)) map)) "\\3"))))))) + +(defun org-footnote-auto-adjust-maybe () + "Renumber and/or sort footnotes according to user settings." + (when (memq org-footnote-auto-adjust '(t renumber)) + (org-footnote-renumber-fn:N)) + (when (memq org-footnote-auto-adjust '(t sort)) + (let ((label (nth 1 (org-footnote-at-definition-p)))) + (org-footnote-normalize 'sort) + (when label + (goto-char (point-min)) + (and (re-search-forward (concat "^\\[" (regexp-quote label) "\\]") + nil t) + (progn (insert " ") + (just-one-space))))))) + (provide 'org-footnote) -;; arch-tag: 1b5954df-fb5d-4da5-8709-78d944dbfc37 ;;; org-footnote.el ends here