Browse Source

org-footnote: rewrite normalize function

* lisp/org-footnote.el (org-footnote-normalize): make use of changes
  to `org-footnote-at-reference-p' and creation of various functions..
  Also comment code.
  (org-footnote-get-next-reference, org-footnote-delete-references,
  org-footnote-delete-definitions): new functions
  (org-footnote-goto-previous-reference, org-footnote-all-labels,
  org-insert-footnote-reference-near-definition, org-footnote-delete):
  rewrite to use org-footnote-get-next-reference.
Nicolas Goaziou 14 years ago
parent
commit
460d093da8
1 changed files with 173 additions and 144 deletions
  1. 173 144
      lisp/org-footnote.el

+ 173 - 144
lisp/org-footnote.el

@@ -211,6 +211,28 @@ label, start, end and definition of the footnote otherwise."
 		    (point-max)))
 		    (point-max)))
 	      (org-trim (buffer-substring (match-end 0) (point))))))))
 	      (org-trim (buffer-substring (match-end 0) (point))))))))
 
 
+(defun org-footnote-get-next-reference (&optional label backward limit)
+  "Return complete reference of the next footnote.
+
+If LABEL is provided, get the next reference of that footnote.  If
+BACKWARD is non-nil, find previous reference instead.  LIMIT is
+the buffer position bounding the search.
+
+Return value is a list like those provided by `org-footnote-at-reference-p'.
+If no footnote is found, return nil."
+  (save-excursion
+    (let* ((label-fmt (if label
+			  (format "\\[%s[]:]" label)
+			(org-re "\\[[-_[:word:]]+[]:]"))))
+      (catch 'exit
+	(while t
+	  (unless (funcall (if backward #'re-search-backward #'re-search-forward)
+			   label-fmt limit t)
+	    (throw 'exit nil))
+	  (unless backward (backward-char))
+	  (when (setq ref (org-footnote-at-reference-p))
+	    (throw 'exit ref)))))))
+
 (defun org-footnote-get-definition (label)
 (defun org-footnote-get-definition (label)
   "Return label, boundaries and definition of the footnote LABEL."
   "Return label, boundaries and definition of the footnote LABEL."
   (let* ((label (regexp-quote (org-footnote-normalize-label label)))
   (let* ((label (regexp-quote (org-footnote-normalize-label label)))
@@ -245,23 +267,19 @@ label, start, end and definition of the footnote otherwise."
   "Find the first closest (to point) reference of footnote with label LABEL."
   "Find the first closest (to point) reference of footnote with label LABEL."
   (interactive "sLabel: ")
   (interactive "sLabel: ")
   (org-mark-ring-push)
   (org-mark-ring-push)
-  (setq label (org-footnote-normalize-label label))
-  (let ((re (format ".\\[%s[]:]" label))
-	(p0 (point)) pos)
+  (let* ((label (org-footnote-normalize-label label)) ref)
     (save-excursion
     (save-excursion
-      (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-backward re nil t))
-		    (and (goto-char (point-max))
-			 (re-search-forward re nil t)))))
-    (if pos
-	(progn
-	  (goto-char (match-end 0))
-	  (org-show-context 'link-search))
-      (error "Cannot find reference of footnote %s" label))))
+      (setq ref (or (org-footnote-get-next-reference label t)
+		    (org-footnote-get-next-reference label)
+		    (save-restriction
+		      (widen)
+		      (or
+		       (org-footnote-get-next-reference label t)
+		       (org-footnote-get-next-reference label))))))
+    (if (not ref)
+	(error "Cannot find reference of footnote %s" label)
+      (goto-char (nth 1 ref))
+      (org-show-context 'link-search))))
 
 
 (defun org-footnote-normalize-label (label)
 (defun org-footnote-normalize-label (label)
   (if (numberp label) (setq label (number-to-string label)))
   (if (numberp label) (setq label (number-to-string label)))
@@ -271,21 +289,20 @@ label, start, end and definition of the footnote otherwise."
 
 
 (defun org-footnote-all-labels ()
 (defun org-footnote-all-labels ()
   "Return list with all defined foot labels used in the buffer."
   "Return list with all defined foot labels used in the buffer."
-  (let (rtn l)
+  (let (rtn l ref)
     (save-excursion
     (save-excursion
       (save-restriction
       (save-restriction
 	(widen)
 	(widen)
+	;; Find all labels found in definitions.
 	(goto-char (point-min))
 	(goto-char (point-min))
 	(while (re-search-forward org-footnote-definition-re nil t)
 	(while (re-search-forward org-footnote-definition-re nil t)
 	  (setq l (org-match-string-no-properties 2))
 	  (setq l (org-match-string-no-properties 2))
 	  (and l (add-to-list 'rtn l)))
 	  (and l (add-to-list 'rtn l)))
+	;; Find all labels found in references.
 	(goto-char (point-min))
 	(goto-char (point-min))
-	(while (re-search-forward org-footnote-re nil t)
-	  (backward-char)
-	  (let* ((full-ref (org-footnote-at-reference-p))
-		 (l (car full-ref)))
-	    ;; Skip anonymous footnotes.
-	    (and l (add-to-list 'rtn l))))))
+	(while (and (setq ref (org-footnote-get-next-reference))
+		    (goto-char (nth 2 ref)))
+	  (and (car ref) (add-to-list 'rtn (car ref))))))
     rtn))
     rtn))
 
 
 (defun org-footnote-unique-label (&optional current)
 (defun org-footnote-unique-label (&optional current)
@@ -419,18 +436,20 @@ With prefix arg SPECIAL, offer additional commands in a menu."
   "See `org-footnote-normalize'.")
   "See `org-footnote-normalize'.")
 
 
 ;;;###autoload
 ;;;###autoload
-(defun org-footnote-normalize (&optional sort-only for-preprocessor)
+(defun org-footnote-normalize (&optional sort-only pre-process-p)
   "Collect the footnotes in various formats and normalize them.
   "Collect the footnotes in various formats and normalize them.
+
 This finds 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
 normalizes them to the usual [N] format that is understood by the
 Org-mode exporters.
 Org-mode exporters.
+
 When SORT-ONLY is set, only sort the footnote definitions into the
 When SORT-ONLY is set, only sort the footnote definitions into the
 referenced sequence.
 referenced sequence.
 
 
-When FOR-PREPROCESSOR is non nil, the default action, is to
-insert normalized footnotes towards the end of the pre-processing
-buffer. Some exporters like docbook, odt etc expect that footnote
-definitions be available before any references to them. Such
+When PRE-PROCESS-P is non-nil, the default action, is to insert
+normalized footnotes towards the end of the pre-processing
+buffer.  Some exporters like docbook, odt etc expect that footnote
+definitions be available before any references to them.  Such
 exporters can let bind `org-footnote-insert-pos-for-preprocessor'
 exporters can let bind `org-footnote-insert-pos-for-preprocessor'
 to symbol 'point-min to achieve the desired behaviour.
 to symbol 'point-min to achieve the desired behaviour.
 
 
@@ -440,6 +459,9 @@ Additional note on `org-footnote-insert-pos-for-preprocessor':
    of pre-processor buffer as witnessed in
    of pre-processor buffer as witnessed in
    `org-export-docbook-get-footnotes'."
    `org-export-docbook-get-footnotes'."
   ;; This is based on Paul's function, but rewritten.
   ;; This is based on Paul's function, but rewritten.
+  ;;
+  ;; Re-create `org-with-limited-levels', but not limited to Org
+  ;; buffers.
   (let* ((limit-level
   (let* ((limit-level
 	  (and (boundp 'org-inlinetask-min-level)
 	  (and (boundp 'org-inlinetask-min-level)
 	       org-inlinetask-min-level
 	       org-inlinetask-min-level
@@ -450,52 +472,53 @@ Additional note on `org-footnote-insert-pos-for-preprocessor':
 			limit-level)))
 			limit-level)))
 	 (outline-regexp
 	 (outline-regexp
 	  (concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ ")))
 	  (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
+	 ;; Determine the highest marker used so far.
+	 (count (if (and pre-process-p org-export-footnotes-markers)
+		    (apply 'max (mapcar 'car org-export-footnotes-markers))
+		  0))
+	 ref-table ins-point ref)
+    (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))
       (goto-char (point-min))
-      (while (re-search-forward org-footnote-re nil t)
-	(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
+      (while (setq ref (org-footnote-get-next-reference))
+	(let* ((lbl (car 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) and definition (DEF)
+	  ;; to REF-TABLE if data was unknown.
+	  (unless a
+	    (let ((def (or (nth 3 ref) ; inline
+			   (and pre-process-p
+				(cdr (assoc lbl org-export-footnotes-data)))
+			   (nth 3 (org-footnote-get-definition lbl)))))
+	      (push (list lbl marker def inlinep) ref-table)))
+	  ;; Remove definition of non-inlined footnotes.
+	  (unless inlinep (org-footnote-delete-definitions lbl))))
+      ;; 2. Find and remove the footnote section, if any. If we are
+      ;; exporting, insert it again at end of buffer. In a non
+      ;; org-mode file, insert instead
+      ;; `org-footnote-tag-for-non-org-mode-files'.
       (goto-char (point-min))
       (goto-char (point-min))
       (cond
       (cond
        ((org-mode-p)
        ((org-mode-p)
@@ -504,14 +527,14 @@ Additional note on `org-footnote-insert-pos-for-preprocessor':
 		  (concat "^\\*[ \t]+" (regexp-quote org-footnote-section)
 		  (concat "^\\*[ \t]+" (regexp-quote org-footnote-section)
 			  "[ \t]*$")
 			  "[ \t]*$")
 		  nil t))
 		  nil t))
-	    (if (or for-preprocessor (not org-footnote-section))
+	    (if pre-process-p
 		(replace-match "")
 		(replace-match "")
 	      (org-back-to-heading t)
 	      (org-back-to-heading t)
 	      (forward-line 1)
 	      (forward-line 1)
 	      (setq ins-point (point))
 	      (setq ins-point (point))
 	      (delete-region (point) (org-end-of-subtree t)))
 	      (delete-region (point) (org-end-of-subtree t)))
 	  (goto-char (point-max))
 	  (goto-char (point-max))
-	  (unless for-preprocessor
+	  (unless pre-process-p
 	    (when org-footnote-section
 	    (when org-footnote-section
 	      (or (bolp) (insert "\n"))
 	      (or (bolp) (insert "\n"))
 	      (insert "* " org-footnote-section "\n")
 	      (insert "* " org-footnote-section "\n")
@@ -528,63 +551,59 @@ Additional note on `org-footnote-insert-pos-for-preprocessor':
 	(delete-region (point) (point-max))
 	(delete-region (point) (point-max))
 	(insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n")
 	(insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n")
 	(setq ins-point (point))))
 	(setq ins-point (point))))
-
-      ;; Insert the footnotes again
-      (goto-char (or (and for-preprocessor
-			  (equal org-footnote-insert-pos-for-preprocessor
-				 'point-min)
-			  (point-min))
-		     ins-point
-		     (point-max)))
-      (setq ref-table (reverse ref-table))
-      (when sort-only
-	;; 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.
+      ;; 3. Clean-up REF-TABLE.
       (setq ref-table
       (setq ref-table
-	    (mapcar
-	     (lambda (x)
-	       (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
+	    (delq nil
+		  (mapcar
 		   (lambda (x)
 		   (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)))))
+		     (cond
+		      ;; When only sorting, ignore inline footnotes.
+		      ((and sort-only (nth 3 x)) nil)
+		      ;; No definition available: provide one.
+		      ((not (nth 2 x))
+		       (append (butlast x 2)
+			       (list (format "DEFINITION NOT FOUND: %s" (car x))
+				     (nth 3 x))))
+		      (t x)))
+		   ref-table)))
+      (setq ref-table (nreverse ref-table))
+      ;; 4. Insert the footnotes again in the buffer, at INS-POINT.
+      (goto-char (or ins-point (point-max)))
+      (cond
+       ((not ref-table))		; no footnote: exit
+       ;; Cases when footnotes should be inserted together in one place.
+       ((or (not (org-mode-p))
+	    org-footnote-section
+	    (not sort-only))
+	(insert "\n"
+		(mapconcat (lambda (x) (format "[%s] %s"
+					  (nth (if sort-only 0 1) x) (nth 2 x)))
+			   ref-table "\n\n")
+		"\n\n")
+       ;; When exporting, add newly insert markers along with their
+       ;; associated definition to `org-export-footnotes-markers'.
+	(when pre-process-p
+	  (setq org-export-footnotes-markers
+		(append (mapcar (lambda (ref) (cons (nth 1 ref) (nth 2 ref)))
+				ref-table)
+			org-export-footnotes-markers))))
+       ;; Else, insert each definition at the end of the section
+       ;; containing their first reference. Happens only in Org
+       ;; files with no special footnote section, and only when
+       ;; doing sorting.
+       (t (mapc 'org-insert-footnote-reference-near-definition
+		ref-table))))))
 
 
 (defun org-insert-footnote-reference-near-definition (entry)
 (defun org-insert-footnote-reference-near-definition (entry)
   "Find first reference of footnote ENTRY and insert the definition there.
   "Find first reference of footnote ENTRY and insert the definition there.
 ENTRY is (fn-label num-mark definition)."
 ENTRY is (fn-label num-mark definition)."
   (when (car entry)
   (when (car entry)
     (goto-char (point-min))
     (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[%s] %s\n" (car entry) (nth 2 entry))))))
+    (let ((ref (org-footnote-get-next-reference (car entry))))
+      (when ref
+	(goto-char (nth 2 ref))
+	(org-footnote-goto-local-insertion-point)
+	(insert (format "\n[%s] %s\n" (car entry) (nth 2 entry)))))))
 
 
 (defun org-footnote-goto-local-insertion-point ()
 (defun org-footnote-goto-local-insertion-point ()
   "Find insertion point for footnote, just before next outline heading."
   "Find insertion point for footnote, just before next outline heading."
@@ -598,6 +617,31 @@ ENTRY is (fn-label num-mark definition)."
   (skip-chars-backward "\n\r\t ")
   (skip-chars-backward "\n\r\t ")
   (forward-line))
   (forward-line))
 
 
+(defun org-footnote-delete-references (label)
+  "Delete every reference to footnote LABEL.
+Return the number of footnotes removed."
+  (save-excursion
+    (goto-char (point-min))
+    (let (ref (nref 0))
+      (while (setq ref (org-footnote-get-next-reference label))
+	(goto-char (nth 1 ref))
+	(delete-region (nth 1 ref) (nth 2 ref))
+	(incf nref))
+      nref)))
+
+(defun org-footnote-delete-definitions (label)
+  "Delete every definition of the footnote LABEL.
+Return the number of footnotes removed."
+  (save-excursion
+    (goto-char (point-min))
+    (let ((def-re (concat "^\\[" (regexp-quote label) "\\]"))
+	  (ndef 0))
+      (while (re-search-forward def-re nil t)
+	(let ((full-def (org-footnote-at-definition-p)))
+	  (delete-region (nth 1 full-def) (nth 2 full-def)))
+	(incf ndef))
+      ndef)))
+
 (defun org-footnote-delete (&optional label)
 (defun org-footnote-delete (&optional label)
   "Delete the footnote at point.
   "Delete the footnote at point.
 This will remove the definition (even multiple definitions if they exist)
 This will remove the definition (even multiple definitions if they exist)
@@ -622,26 +666,11 @@ If LABEL is non-nil, delete that footnote instead."
 		   ((setq x (org-footnote-at-definition-p))
 		   ((setq x (org-footnote-at-definition-p))
 		    (car x))
 		    (car x))
 		   (t (error "Don't know which footnote to remove")))))
 		   (t (error "Don't know which footnote to remove")))))
-      ;; 2. Now that LABEL is non-nil, find every reference to it and
-      ;;    delete it. Increase counter NREF in the process.
-      (save-excursion
-	(save-restriction
-	  (goto-char (point-min))
-	  (while (re-search-forward org-footnote-re nil t)
-	    (backward-char)
-	    (let* ((full-ref (org-footnote-at-reference-p))
-		   (l (car full-ref)))
-	      (when (equal l label)
-		(delete-region (nth 1 full-ref) (nth 2 full-ref))
-		(incf nref))))
-	  ;; 3. Find every definition of footnote LABEL and delete it.
-	  ;;    Increase counter NDEF in the process.
-	  (goto-char (point-min))
-	  (let ((def-re (concat "^\\[" (regexp-quote label) "\\]")) beg)
-	    (while (re-search-forward def-re nil t)
-	      (let ((full-def (org-footnote-at-definition-p)))
-		(delete-region (nth 1 full-def) (nth 2 full-def)))
-	      (incf ndef)))))
+      ;; 2. Now that LABEL is non-nil, find every reference and every
+      ;; definition, and delete them.
+      (setq nref (org-footnote-delete-references label)
+	    ndef (org-footnote-delete-definitions label))
+      ;; 3. Verify consistency of footnotes and notify user.
       (org-footnote-auto-adjust-maybe)
       (org-footnote-auto-adjust-maybe)
       (message "%d definition(s) of and %d reference(s) of footnote %s removed"
       (message "%d definition(s) of and %d reference(s) of footnote %s removed"
 	       ndef nref label))))
 	       ndef nref label))))