Commit | Line | Data |
---|---|---|
0bd48b37 CD |
1 | ;;; org-footnote.el --- Footnote support in Org and elsewhere |
2 | ;; | |
ba318903 | 3 | ;; Copyright (C) 2009-2014 Free Software Foundation, Inc. |
0bd48b37 CD |
4 | ;; |
5 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
6 | ;; Keywords: outlines, hypermedia, calendar, wp | |
7 | ;; Homepage: http://orgmode.org | |
0bd48b37 CD |
8 | ;; |
9 | ;; This file is part of GNU Emacs. | |
10 | ;; | |
11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation, either version 3 of the License, or | |
14 | ;; (at your option) any later version. | |
15 | ||
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
24 | ;; | |
25 | ;;; Commentary: | |
26 | ||
27 | ;; This file contains the code dealing with footnotes in Org-mode. | |
28 | ;; The code can also be used in arbitrary text modes to provide | |
29 | ;; footnotes. Compared to Steven L Baur's footnote.el it provides | |
30 | ;; better support for resuming editing. It is less configurable than | |
31 | ;; Steve's code, though. | |
32 | ||
33 | ;;; Code: | |
34 | ||
35 | (eval-when-compile | |
36 | (require 'cl)) | |
37 | (require 'org-macs) | |
38 | (require 'org-compat) | |
39 | ||
e66ba1df BG |
40 | (declare-function message-point-in-header-p "message" ()) |
41 | (declare-function org-back-over-empty-lines "org" ()) | |
42 | (declare-function org-back-to-heading "org" (&optional invisible-ok)) | |
3ab2c837 | 43 | (declare-function org-combine-plists "org" (&rest plists)) |
e66ba1df | 44 | (declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading)) |
e66ba1df BG |
45 | (declare-function org-fill-paragraph "org" (&optional justify)) |
46 | (declare-function org-icompleting-read "org" (&rest args)) | |
47 | (declare-function org-id-uuid "org-id" ()) | |
48 | (declare-function org-in-block-p "org" (names)) | |
54a0dee5 | 49 | (declare-function org-in-commented-line "org" ()) |
3ab2c837 | 50 | (declare-function org-in-indented-comment-line "org" ()) |
0bd48b37 | 51 | (declare-function org-in-regexp "org" (re &optional nlines visually)) |
8bfe682a | 52 | (declare-function org-in-verbatim-emphasis "org" ()) |
e66ba1df | 53 | (declare-function org-inside-LaTeX-fragment-p "org" ()) |
ed21c5c8 | 54 | (declare-function org-inside-latex-macro-p "org" ()) |
e66ba1df BG |
55 | (declare-function org-mark-ring-push "org" (&optional pos buffer)) |
56 | (declare-function org-show-context "org" (&optional key)) | |
57 | (declare-function org-trim "org" (s)) | |
8223b1d2 | 58 | (declare-function org-skip-whitespace "org" ()) |
e66ba1df | 59 | (declare-function outline-next-heading "outline") |
153ae947 | 60 | (declare-function org-skip-whitespace "org" ()) |
3ab2c837 | 61 | |
e66ba1df BG |
62 | (defvar org-outline-regexp-bol) ; defined in org.el |
63 | (defvar org-odd-levels-only) ; defined in org.el | |
64 | (defvar org-bracket-link-regexp) ; defined in org.el | |
65 | (defvar message-cite-prefix-regexp) ; defined in message.el | |
66 | (defvar message-signature-separator) ; defined in message.el | |
0bd48b37 CD |
67 | |
68 | (defconst org-footnote-re | |
3ab2c837 BG |
69 | ;; Only [1]-like footnotes are closed in this regexp, as footnotes |
70 | ;; from other types might contain square brackets (i.e. links) in | |
71 | ;; their definition. | |
72 | ;; | |
73 | ;; `org-re' is used for regexp compatibility with XEmacs. | |
e66ba1df BG |
74 | (concat "\\[\\(?:" |
75 | ;; Match inline footnotes. | |
76 | (org-re "fn:\\([-_[:word:]]+\\)?:\\|") | |
77 | ;; Match other footnotes. | |
78 | "\\(?:\\([0-9]+\\)\\]\\)\\|" | |
79 | (org-re "\\(fn:[-_[:word:]]+\\)") | |
80 | "\\)") | |
0bd48b37 CD |
81 | "Regular expression for matching footnotes.") |
82 | ||
c8d0cf5c | 83 | (defconst org-footnote-definition-re |
e66ba1df | 84 | (org-re "^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]") |
0bd48b37 CD |
85 | "Regular expression matching the definition of a footnote.") |
86 | ||
153ae947 | 87 | (defconst org-footnote-forbidden-blocks |
271672fa | 88 | '("ascii" "beamer" "comment" "example" "html" "latex" "odt" "src") |
3ab2c837 BG |
89 | "Names of blocks where footnotes are not allowed.") |
90 | ||
86fbb8ca CD |
91 | (defgroup org-footnote nil |
92 | "Footnotes in Org-mode." | |
93 | :tag "Org Footnote" | |
94 | :group 'org) | |
95 | ||
0bd48b37 | 96 | (defcustom org-footnote-section "Footnotes" |
271672fa BG |
97 | "Outline heading containing footnote definitions. |
98 | ||
99 | This can be nil, to place footnotes locally at the end of the | |
100 | current outline node. If can also be the name of a special | |
101 | outline heading under which footnotes should be put. | |
102 | ||
0bd48b37 | 103 | This variable defines the place where Org puts the definition |
271672fa BG |
104 | automatically, i.e. when creating the footnote, and when sorting |
105 | the notes. However, by hand you may place definitions | |
106 | *anywhere*. | |
107 | ||
108 | If this is a string, during export, all subtrees starting with | |
109 | this heading will be ignored." | |
86fbb8ca | 110 | :group 'org-footnote |
0bd48b37 | 111 | :type '(choice |
8bfe682a | 112 | (string :tag "Collect footnotes under heading") |
0bd48b37 CD |
113 | (const :tag "Define footnotes locally" nil))) |
114 | ||
115 | (defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:" | |
116 | "Tag marking the beginning of footnote section. | |
e66ba1df BG |
117 | The Org footnote engine can be used in arbitrary text files as well |
118 | as in Org-mode. Outside Org mode, new footnotes are always placed at | |
0bd48b37 CD |
119 | the end of the file. When you normalize the notes, any line containing |
120 | only this tag will be removed, a new one will be inserted at the end | |
e66ba1df BG |
121 | of the file, followed by the collected and normalized footnotes. |
122 | ||
123 | If you don't want any tag in such buffers, set this variable to nil." | |
86fbb8ca | 124 | :group 'org-footnote |
e66ba1df BG |
125 | :type '(choice |
126 | (string :tag "Collect footnotes under tag") | |
127 | (const :tag "Don't use a tag" nil))) | |
0bd48b37 CD |
128 | |
129 | (defcustom org-footnote-define-inline nil | |
ed21c5c8 | 130 | "Non-nil means define footnotes inline, at reference location. |
0bd48b37 CD |
131 | When nil, footnotes will be defined in a special section near |
132 | the end of the document. When t, the [fn:label:definition] notation | |
133 | will be used to define the footnote at the reference position." | |
134 | :group 'org-footnote | |
135 | :type 'boolean) | |
136 | ||
137 | (defcustom org-footnote-auto-label t | |
ed21c5c8 | 138 | "Non-nil means define automatically new labels for footnotes. |
0bd48b37 CD |
139 | Possible values are: |
140 | ||
271672fa BG |
141 | nil Prompt the user for each label. |
142 | t Create unique labels of the form [fn:1], [fn:2], etc. | |
143 | confirm Like t, but let the user edit the created value. | |
144 | The label can be removed from the minibuffer to create | |
0bd48b37 | 145 | an anonymous footnote. |
3ab2c837 | 146 | random Automatically generate a unique, random label. |
271672fa | 147 | plain Automatically create plain number labels like [1]." |
0bd48b37 CD |
148 | :group 'org-footnote |
149 | :type '(choice | |
8bfe682a | 150 | (const :tag "Prompt for label" nil) |
0bd48b37 CD |
151 | (const :tag "Create automatic [fn:N]" t) |
152 | (const :tag "Offer automatic [fn:N] for editing" confirm) | |
3ab2c837 | 153 | (const :tag "Create a random label" random) |
0bd48b37 CD |
154 | (const :tag "Create automatic [N]" plain))) |
155 | ||
c8d0cf5c | 156 | (defcustom org-footnote-auto-adjust nil |
ed21c5c8 | 157 | "Non-nil means automatically adjust footnotes after insert/delete. |
c8d0cf5c CD |
158 | When this is t, after each insertion or deletion of a footnote, |
159 | simple fn:N footnotes will be renumbered, and all footnotes will be sorted. | |
160 | If you want to have just sorting or just renumbering, set this variable | |
161 | to `sort' or `renumber'. | |
162 | ||
163 | The main values of this variable can be set with in-buffer options: | |
164 | ||
165 | #+STARTUP: fnadjust | |
166 | #+STARTUP: nofnadjust" | |
167 | :group 'org-footnote | |
168 | :type '(choice | |
271672fa | 169 | (const :tag "No adjustment" nil) |
c8d0cf5c CD |
170 | (const :tag "Renumber" renumber) |
171 | (const :tag "Sort" sort) | |
172 | (const :tag "Renumber and Sort" t))) | |
173 | ||
0bd48b37 | 174 | (defcustom org-footnote-fill-after-inline-note-extraction nil |
ed21c5c8 | 175 | "Non-nil means fill paragraphs after extracting footnotes. |
0bd48b37 CD |
176 | When extracting inline footnotes, the lengths of lines can change a lot. |
177 | When this option is set, paragraphs from which an inline footnote has been | |
178 | extracted will be filled again." | |
179 | :group 'org-footnote | |
180 | :type 'boolean) | |
181 | ||
3ab2c837 BG |
182 | (defun org-footnote-in-valid-context-p () |
183 | "Is point in a context where footnotes are allowed?" | |
184 | (save-match-data | |
185 | (not (or (org-in-commented-line) | |
186 | (org-in-indented-comment-line) | |
e66ba1df | 187 | (org-inside-LaTeX-fragment-p) |
3ab2c837 | 188 | ;; Avoid literal example. |
e66ba1df | 189 | (org-in-verbatim-emphasis) |
3ab2c837 BG |
190 | (save-excursion |
191 | (beginning-of-line) | |
192 | (looking-at "[ \t]*:[ \t]+")) | |
193 | ;; Avoid cited text and headers in message-mode. | |
194 | (and (derived-mode-p 'message-mode) | |
195 | (or (save-excursion | |
196 | (beginning-of-line) | |
197 | (looking-at message-cite-prefix-regexp)) | |
198 | (message-point-in-header-p))) | |
199 | ;; Avoid forbidden blocks. | |
200 | (org-in-block-p org-footnote-forbidden-blocks))))) | |
201 | ||
0bd48b37 CD |
202 | (defun org-footnote-at-reference-p () |
203 | "Is the cursor at a footnote reference? | |
3ab2c837 BG |
204 | |
205 | If so, return a list containing its label, beginning and ending | |
206 | positions, and the definition, when inlined." | |
207 | (when (and (org-footnote-in-valid-context-p) | |
208 | (or (looking-at org-footnote-re) | |
209 | (org-in-regexp org-footnote-re) | |
210 | (save-excursion (re-search-backward org-footnote-re nil t))) | |
e66ba1df | 211 | (/= (match-beginning 0) (point-at-bol))) |
3ab2c837 | 212 | (let* ((beg (match-beginning 0)) |
e66ba1df BG |
213 | (label (or (org-match-string-no-properties 2) |
214 | (org-match-string-no-properties 3) | |
3ab2c837 | 215 | ;; Anonymous footnotes don't have labels |
e66ba1df BG |
216 | (and (match-string 1) |
217 | (concat "fn:" (org-match-string-no-properties 1))))) | |
3ab2c837 BG |
218 | ;; Inline footnotes don't end at (match-end 0) as |
219 | ;; `org-footnote-re' stops just after the second colon. | |
220 | ;; Find the real ending with `scan-sexps', so Org doesn't | |
221 | ;; get fooled by unrelated closing square brackets. | |
222 | (end (ignore-errors (scan-sexps beg 1)))) | |
223 | ;; Point is really at a reference if it's located before true | |
224 | ;; ending of the footnote. | |
225 | (when (and end (< (point) end) | |
226 | ;; Verify match isn't a part of a link. | |
227 | (not (save-excursion | |
228 | (goto-char beg) | |
229 | (let ((linkp | |
230 | (save-match-data | |
231 | (org-in-regexp org-bracket-link-regexp)))) | |
232 | (and linkp (< (point) (cdr linkp)))))) | |
233 | ;; Verify point doesn't belong to a LaTeX macro. | |
271672fa | 234 | (not (org-inside-latex-macro-p))) |
3ab2c837 BG |
235 | (list label beg end |
236 | ;; Definition: ensure this is an inline footnote first. | |
237 | (and (or (not label) (match-string 1)) | |
e66ba1df BG |
238 | (org-trim (buffer-substring-no-properties |
239 | (match-end 0) (1- end))))))))) | |
0bd48b37 CD |
240 | |
241 | (defun org-footnote-at-definition-p () | |
153ae947 | 242 | "Is point within a footnote definition? |
3ab2c837 | 243 | |
153ae947 BG |
244 | This matches only pure definitions like [1] or [fn:name] at the |
245 | beginning of a line. It does not match references like | |
246 | \[fn:name:definition], where the footnote text is included and | |
247 | defined locally. | |
3ab2c837 | 248 | |
153ae947 BG |
249 | The return value will be nil if not at a footnote definition, and |
250 | a list with label, start, end and definition of the footnote | |
251 | otherwise." | |
e66ba1df | 252 | (when (save-excursion (beginning-of-line) (org-footnote-in-valid-context-p)) |
3ab2c837 BG |
253 | (save-excursion |
254 | (end-of-line) | |
271672fa BG |
255 | ;; Footnotes definitions are separated by new headlines, another |
256 | ;; footnote definition or 2 blank lines. | |
257 | (let ((lim (save-excursion | |
258 | (re-search-backward | |
259 | (concat org-outline-regexp-bol | |
260 | "\\|^\\([ \t]*\n\\)\\{2,\\}") nil t)))) | |
3ab2c837 | 261 | (when (re-search-backward org-footnote-definition-re lim t) |
153ae947 BG |
262 | (let ((label (org-match-string-no-properties 1)) |
263 | (beg (match-beginning 0)) | |
264 | (beg-def (match-end 0)) | |
265 | ;; In message-mode, do not search after signature. | |
266 | (end (let ((bound (and (derived-mode-p 'message-mode) | |
267 | (save-excursion | |
268 | (goto-char (point-max)) | |
269 | (re-search-backward | |
270 | message-signature-separator nil t))))) | |
271 | (if (progn | |
272 | (end-of-line) | |
273 | (re-search-forward | |
e66ba1df BG |
274 | (concat org-outline-regexp-bol "\\|" |
275 | org-footnote-definition-re "\\|" | |
271672fa | 276 | "^\\([ \t]*\n\\)\\{2,\\}") bound 'move)) |
8223b1d2 | 277 | (match-beginning 0) |
153ae947 BG |
278 | (point))))) |
279 | (list label beg end | |
280 | (org-trim (buffer-substring-no-properties beg-def end))))))))) | |
3ab2c837 BG |
281 | |
282 | (defun org-footnote-get-next-reference (&optional label backward limit) | |
283 | "Return complete reference of the next footnote. | |
284 | ||
285 | If LABEL is provided, get the next reference of that footnote. If | |
286 | BACKWARD is non-nil, find previous reference instead. LIMIT is | |
287 | the buffer position bounding the search. | |
288 | ||
289 | Return value is a list like those provided by `org-footnote-at-reference-p'. | |
290 | If no footnote is found, return nil." | |
0bd48b37 | 291 | (save-excursion |
3ab2c837 BG |
292 | (let* ((label-fmt (if label (format "\\[%s[]:]" label) org-footnote-re))) |
293 | (catch 'exit | |
294 | (while t | |
295 | (unless (funcall (if backward #'re-search-backward #'re-search-forward) | |
296 | label-fmt limit t) | |
297 | (throw 'exit nil)) | |
298 | (unless backward (backward-char)) | |
299 | (let ((ref (org-footnote-at-reference-p))) | |
300 | (when ref (throw 'exit ref)))))))) | |
301 | ||
302 | (defun org-footnote-next-reference-or-definition (limit) | |
303 | "Move point to next footnote reference or definition. | |
304 | ||
305 | LIMIT is the buffer position bounding the search. | |
306 | ||
307 | Return value is a list like those provided by | |
308 | `org-footnote-at-reference-p' or `org-footnote-at-definition-p'. | |
309 | If no footnote is found, return nil." | |
e66ba1df | 310 | (let* (ref (origin (point))) |
3ab2c837 BG |
311 | (catch 'exit |
312 | (while t | |
313 | (unless (re-search-forward org-footnote-re limit t) | |
e66ba1df | 314 | (goto-char origin) |
3ab2c837 BG |
315 | (throw 'exit nil)) |
316 | ;; Beware: with [1]-like footnotes point will be just after | |
317 | ;; the closing square bracket. | |
318 | (backward-char) | |
319 | (cond | |
320 | ((setq ref (org-footnote-at-reference-p)) | |
321 | (throw 'exit ref)) | |
322 | ;; Definition: also grab the last square bracket, only | |
323 | ;; matched in `org-footnote-re' for [1]-like footnotes. | |
324 | ((save-match-data (org-footnote-at-definition-p)) | |
325 | (let ((end (match-end 0))) | |
326 | (throw 'exit | |
327 | (list nil (match-beginning 0) | |
328 | (if (eq (char-before end) 93) end (1+ end))))))))))) | |
329 | ||
330 | (defun org-footnote-get-definition (label) | |
331 | "Return label, boundaries and definition of the footnote LABEL." | |
332 | (let* ((label (regexp-quote (org-footnote-normalize-label label))) | |
333 | (re (format "^\\[%s\\]\\|.\\[%s:" label label)) | |
334 | pos) | |
335 | (save-excursion | |
e66ba1df BG |
336 | (save-restriction |
337 | (when (or (re-search-forward re nil t) | |
338 | (and (goto-char (point-min)) | |
339 | (re-search-forward re nil t)) | |
340 | (and (progn (widen) t) | |
341 | (goto-char (point-min)) | |
342 | (re-search-forward re nil t))) | |
343 | (let ((refp (org-footnote-at-reference-p))) | |
344 | (cond | |
345 | ((and (nth 3 refp) refp)) | |
346 | ((org-footnote-at-definition-p))))))))) | |
0bd48b37 CD |
347 | |
348 | (defun org-footnote-goto-definition (label) | |
e66ba1df BG |
349 | "Move point to the definition of the footnote LABEL. |
350 | Return a non-nil value when a definition has been found." | |
0bd48b37 CD |
351 | (interactive "sLabel: ") |
352 | (org-mark-ring-push) | |
3ab2c837 BG |
353 | (let ((def (org-footnote-get-definition label))) |
354 | (if (not def) | |
0bd48b37 | 355 | (error "Cannot find definition of footnote %s" label) |
3ab2c837 BG |
356 | (goto-char (nth 1 def)) |
357 | (looking-at (format "\\[%s\\]\\|\\[%s:" label label)) | |
358 | (goto-char (match-end 0)) | |
0bd48b37 | 359 | (org-show-context 'link-search) |
8223b1d2 | 360 | (when (derived-mode-p 'org-mode) |
e66ba1df BG |
361 | (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")) |
362 | t))) | |
0bd48b37 | 363 | |
86fbb8ca | 364 | (defun org-footnote-goto-previous-reference (label) |
afe98dfa | 365 | "Find the first closest (to point) reference of footnote with label LABEL." |
0bd48b37 CD |
366 | (interactive "sLabel: ") |
367 | (org-mark-ring-push) | |
3ab2c837 | 368 | (let* ((label (org-footnote-normalize-label label)) ref) |
0bd48b37 | 369 | (save-excursion |
3ab2c837 BG |
370 | (setq ref (or (org-footnote-get-next-reference label t) |
371 | (org-footnote-get-next-reference label) | |
372 | (save-restriction | |
373 | (widen) | |
374 | (or | |
375 | (org-footnote-get-next-reference label t) | |
376 | (org-footnote-get-next-reference label)))))) | |
377 | (if (not ref) | |
378 | (error "Cannot find reference of footnote %s" label) | |
379 | (goto-char (nth 1 ref)) | |
380 | (org-show-context 'link-search)))) | |
0bd48b37 CD |
381 | |
382 | (defun org-footnote-normalize-label (label) | |
3ab2c837 BG |
383 | "Return LABEL as an appropriate string." |
384 | (cond | |
385 | ((numberp label) (number-to-string label)) | |
386 | ((equal "" label) nil) | |
387 | ((not (string-match "^[0-9]+$\\|^fn:" label)) | |
388 | (concat "fn:" label)) | |
389 | (t label))) | |
390 | ||
391 | (defun org-footnote-all-labels (&optional with-defs) | |
392 | "Return list with all defined foot labels used in the buffer. | |
393 | ||
394 | If WITH-DEFS is non-nil, also associate the definition to each | |
395 | label. The function will then return an alist whose key is label | |
396 | and value definition." | |
397 | (let* (rtn | |
398 | (push-to-rtn | |
399 | (function | |
400 | ;; Depending on WITH-DEFS, store label or (label . def) of | |
401 | ;; footnote reference/definition given as argument in RTN. | |
402 | (lambda (el) | |
403 | (let ((lbl (car el))) | |
404 | (push (if with-defs (cons lbl (nth 3 el)) lbl) rtn)))))) | |
0bd48b37 CD |
405 | (save-excursion |
406 | (save-restriction | |
407 | (widen) | |
3ab2c837 | 408 | ;; Find all labels found in definitions. |
0bd48b37 | 409 | (goto-char (point-min)) |
3ab2c837 BG |
410 | (let (def) |
411 | (while (re-search-forward org-footnote-definition-re nil t) | |
412 | (when (setq def (org-footnote-at-definition-p)) | |
413 | (funcall push-to-rtn def)))) | |
414 | ;; Find all labels found in references. | |
0bd48b37 | 415 | (goto-char (point-min)) |
3ab2c837 BG |
416 | (let (ref) |
417 | (while (setq ref (org-footnote-get-next-reference)) | |
418 | (goto-char (nth 2 ref)) | |
419 | (and (car ref) ; ignore anonymous footnotes | |
420 | (not (funcall (if with-defs #'assoc #'member) (car ref) rtn)) | |
421 | (funcall push-to-rtn ref)))))) | |
0bd48b37 CD |
422 | rtn)) |
423 | ||
424 | (defun org-footnote-unique-label (&optional current) | |
425 | "Return a new unique footnote label. | |
e66ba1df BG |
426 | |
427 | The function returns the first \"fn:N\" or \"N\" label that is | |
428 | currently not used. | |
429 | ||
430 | Optional argument CURRENT is the list of labels active in the | |
431 | buffer." | |
0bd48b37 CD |
432 | (unless current (setq current (org-footnote-all-labels))) |
433 | (let ((fmt (if (eq org-footnote-auto-label 'plain) "%d" "fn:%d")) | |
434 | (cnt 1)) | |
435 | (while (member (format fmt cnt) current) | |
436 | (incf cnt)) | |
437 | (format fmt cnt))) | |
438 | ||
0bd48b37 CD |
439 | (defun org-footnote-new () |
440 | "Insert a new footnote. | |
441 | This command prompts for a label. If this is a label referencing an | |
442 | existing label, only insert the label. If the footnote label is empty | |
443 | or new, let the user edit the definition of the footnote." | |
444 | (interactive) | |
e66ba1df | 445 | (unless (org-footnote-in-valid-context-p) |
3ab2c837 | 446 | (error "Cannot insert a footnote here")) |
e66ba1df BG |
447 | (let* ((lbls (and (not (equal org-footnote-auto-label 'random)) |
448 | (org-footnote-all-labels))) | |
8223b1d2 BG |
449 | (propose (and (not (equal org-footnote-auto-label 'random)) |
450 | (org-footnote-unique-label lbls))) | |
0bd48b37 | 451 | (label |
3ab2c837 BG |
452 | (org-footnote-normalize-label |
453 | (cond | |
454 | ((member org-footnote-auto-label '(t plain)) | |
455 | propose) | |
456 | ((equal org-footnote-auto-label 'random) | |
457 | (require 'org-id) | |
458 | (substring (org-id-uuid) 0 8)) | |
459 | (t | |
e66ba1df | 460 | (org-icompleting-read |
3ab2c837 | 461 | "Label (leave empty for anonymous): " |
e66ba1df BG |
462 | (mapcar 'list lbls) nil nil |
463 | (if (eq org-footnote-auto-label 'confirm) propose nil))))))) | |
0bd48b37 | 464 | (cond |
e66ba1df | 465 | ((bolp) (error "Cannot create a footnote reference at left margin")) |
3ab2c837 | 466 | ((not label) |
0bd48b37 CD |
467 | (insert "[fn:: ]") |
468 | (backward-char 1)) | |
e66ba1df | 469 | ((member label lbls) |
0bd48b37 CD |
470 | (insert "[" label "]") |
471 | (message "New reference to existing note")) | |
472 | (org-footnote-define-inline | |
473 | (insert "[" label ": ]") | |
c8d0cf5c CD |
474 | (backward-char 1) |
475 | (org-footnote-auto-adjust-maybe)) | |
0bd48b37 CD |
476 | (t |
477 | (insert "[" label "]") | |
c8d0cf5c CD |
478 | (org-footnote-create-definition label) |
479 | (org-footnote-auto-adjust-maybe))))) | |
0bd48b37 | 480 | |
6a24cbb1 | 481 | (defvar org-blank-before-new-entry) ; silence byte-compiler |
0bd48b37 CD |
482 | (defun org-footnote-create-definition (label) |
483 | "Start the definition of a footnote with label LABEL." | |
484 | (interactive "sLabel: ") | |
d3517077 BG |
485 | (let ((label (org-footnote-normalize-label label)) |
486 | electric-indent-mode) ;; Prevent wrong indentation | |
0bd48b37 | 487 | (cond |
e66ba1df | 488 | ;; In an Org file. |
8223b1d2 | 489 | ((derived-mode-p 'org-mode) |
e66ba1df BG |
490 | ;; If `org-footnote-section' is defined, find it, or create it |
491 | ;; at the end of the buffer. | |
3ab2c837 BG |
492 | (when org-footnote-section |
493 | (goto-char (point-min)) | |
494 | (let ((re (concat "^\\*+[ \t]+" org-footnote-section "[ \t]*$"))) | |
495 | (unless (or (re-search-forward re nil t) | |
496 | (and (progn (widen) t) | |
497 | (re-search-forward re nil t))) | |
e66ba1df BG |
498 | (goto-char (point-max)) |
499 | (skip-chars-backward " \t\r\n") | |
500 | (unless (bolp) (newline)) | |
501 | ;; Insert new section. Separate it from the previous one | |
502 | ;; with a blank line, unless `org-blank-before-new-entry' | |
503 | ;; explicitly says no. | |
504 | (when (and (cdr (assq 'heading org-blank-before-new-entry)) | |
505 | (zerop (save-excursion (org-back-over-empty-lines)))) | |
506 | (insert "\n")) | |
507 | (insert "* " org-footnote-section "\n")))) | |
508 | ;; Move to the end of this entry (which may be | |
509 | ;; `org-footnote-section' or the current one). | |
8d642074 CD |
510 | (org-footnote-goto-local-insertion-point) |
511 | (org-show-context 'link-search)) | |
0bd48b37 | 512 | (t |
3ab2c837 | 513 | ;; In a non-Org file. Search for footnote tag, or create it if |
e66ba1df | 514 | ;; specified (at the end of buffer, or before signature if in |
3ab2c837 | 515 | ;; Message mode). Set point after any definition already there. |
e66ba1df BG |
516 | (let ((tag (and org-footnote-tag-for-non-org-mode-files |
517 | (concat "^" (regexp-quote | |
518 | org-footnote-tag-for-non-org-mode-files) | |
519 | "[ \t]*$"))) | |
520 | (max (if (and (derived-mode-p 'message-mode) | |
521 | (goto-char (point-max)) | |
522 | (re-search-backward | |
523 | message-signature-separator nil t)) | |
524 | (progn | |
525 | ;; Ensure one blank line separates last | |
526 | ;; footnote from signature. | |
527 | (beginning-of-line) | |
528 | (open-line 2) | |
529 | (point-marker)) | |
530 | (point-max-marker)))) | |
531 | (set-marker-insertion-type max t) | |
3ab2c837 | 532 | (goto-char max) |
e66ba1df BG |
533 | ;; Check if the footnote tag is defined but missing. In this |
534 | ;; case, insert it, before any footnote or one blank line | |
535 | ;; after any previous text. | |
536 | (when (and tag (not (re-search-backward tag nil t))) | |
afe98dfa | 537 | (skip-chars-backward " \t\r\n") |
e66ba1df BG |
538 | (while (re-search-backward org-footnote-definition-re nil t)) |
539 | (unless (bolp) (newline 2)) | |
540 | (insert org-footnote-tag-for-non-org-mode-files "\n\n")) | |
541 | ;; Remove superfluous white space and clear marker. | |
542 | (goto-char max) | |
543 | (skip-chars-backward " \t\r\n") | |
544 | (delete-region (point) max) | |
545 | (unless (bolp) (newline)) | |
3ab2c837 | 546 | (set-marker max nil)))) |
e66ba1df | 547 | ;; Insert footnote label. |
153ae947 BG |
548 | (when (zerop (org-back-over-empty-lines)) (newline)) |
549 | (insert "[" label "] \n") | |
550 | (backward-char) | |
e66ba1df BG |
551 | ;; Only notify user about next possible action when in an Org |
552 | ;; buffer, as the bindings may have different meanings otherwise. | |
8223b1d2 | 553 | (when (derived-mode-p 'org-mode) |
e66ba1df BG |
554 | (message |
555 | "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")))) | |
0bd48b37 CD |
556 | |
557 | ;;;###autoload | |
558 | (defun org-footnote-action (&optional special) | |
559 | "Do the right thing for footnotes. | |
3ab2c837 BG |
560 | |
561 | When at a footnote reference, jump to the definition. | |
562 | ||
563 | When at a definition, jump to the references if they exist, offer | |
564 | to create them otherwise. | |
565 | ||
566 | When neither at definition or reference, create a new footnote, | |
567 | interactively. | |
568 | ||
0bd48b37 CD |
569 | With prefix arg SPECIAL, offer additional commands in a menu." |
570 | (interactive "P") | |
571 | (let (tmp c) | |
572 | (cond | |
573 | (special | |
c8d0cf5c | 574 | (message "Footnotes: [s]ort | [r]enumber fn:N | [S]=r+s |->[n]umeric | [d]elete") |
0bd48b37 CD |
575 | (setq c (read-char-exclusive)) |
576 | (cond | |
3ab2c837 BG |
577 | ((eq c ?s) (org-footnote-normalize 'sort)) |
578 | ((eq c ?r) (org-footnote-renumber-fn:N)) | |
579 | ((eq c ?S) | |
c8d0cf5c CD |
580 | (org-footnote-renumber-fn:N) |
581 | (org-footnote-normalize 'sort)) | |
3ab2c837 BG |
582 | ((eq c ?n) (org-footnote-normalize)) |
583 | ((eq c ?d) (org-footnote-delete)) | |
0bd48b37 CD |
584 | (t (error "No such footnote command %c" c)))) |
585 | ((setq tmp (org-footnote-at-reference-p)) | |
3ab2c837 BG |
586 | (cond |
587 | ;; Anonymous footnote: move point at the beginning of its | |
588 | ;; definition. | |
589 | ((not (car tmp)) | |
590 | (goto-char (nth 1 tmp)) | |
591 | (forward-char 5)) | |
592 | ;; A definition exists: move to it. | |
593 | ((ignore-errors (org-footnote-goto-definition (car tmp)))) | |
594 | ;; No definition exists: offer to create it. | |
595 | ((yes-or-no-p (format "No definition for %s. Create one? " (car tmp))) | |
596 | (org-footnote-create-definition (car tmp))))) | |
0bd48b37 | 597 | ((setq tmp (org-footnote-at-definition-p)) |
3ab2c837 | 598 | (org-footnote-goto-previous-reference (car tmp))) |
0bd48b37 CD |
599 | (t (org-footnote-new))))) |
600 | ||
601 | ;;;###autoload | |
271672fa | 602 | (defun org-footnote-normalize (&optional sort-only) |
0bd48b37 | 603 | "Collect the footnotes in various formats and normalize them. |
3ab2c837 | 604 | |
c8d0cf5c | 605 | This finds the different sorts of footnotes allowed in Org, and |
271672fa | 606 | normalizes them to the usual [N] format. |
3ab2c837 | 607 | |
0bd48b37 | 608 | When SORT-ONLY is set, only sort the footnote definitions into the |
271672fa | 609 | referenced sequence." |
0bd48b37 | 610 | ;; This is based on Paul's function, but rewritten. |
3ab2c837 BG |
611 | ;; |
612 | ;; Re-create `org-with-limited-levels', but not limited to Org | |
613 | ;; buffers. | |
c8d0cf5c CD |
614 | (let* ((limit-level |
615 | (and (boundp 'org-inlinetask-min-level) | |
616 | org-inlinetask-min-level | |
617 | (1- org-inlinetask-min-level))) | |
618 | (nstars (and limit-level | |
271672fa | 619 | (if org-odd-levels-only (1- (* limit-level 2)) |
c8d0cf5c | 620 | limit-level))) |
3ab2c837 | 621 | (org-outline-regexp |
c8d0cf5c | 622 | (concat "\\*" (if nstars (format "\\{1,%d\\} " nstars) "+ "))) |
271672fa BG |
623 | (count 0) |
624 | ins-point ref ref-table) | |
3ab2c837 BG |
625 | (save-excursion |
626 | ;; 1. Find every footnote reference, extract the definition, and | |
627 | ;; collect that data in REF-TABLE. If SORT-ONLY is nil, also | |
628 | ;; normalize references. | |
0bd48b37 | 629 | (goto-char (point-min)) |
3ab2c837 BG |
630 | (while (setq ref (org-footnote-get-next-reference)) |
631 | (let* ((lbl (car ref)) | |
153ae947 | 632 | (pos (nth 1 ref)) |
3ab2c837 BG |
633 | ;; When footnote isn't anonymous, check if it's label |
634 | ;; (REF) is already stored in REF-TABLE. In that case, | |
635 | ;; extract number used to identify it (MARKER). If | |
636 | ;; footnote is unknown, increment the global counter | |
637 | ;; (COUNT) to create an unused identifier. | |
638 | (a (and lbl (assoc lbl ref-table))) | |
639 | (marker (or (nth 1 a) (incf count))) | |
640 | ;; Is the reference inline or pointing to an inline | |
641 | ;; footnote? | |
642 | (inlinep (or (stringp (nth 3 ref)) (nth 3 a)))) | |
643 | ;; Replace footnote reference with [MARKER]. Maybe fill | |
644 | ;; paragraph once done. If SORT-ONLY is non-nil, only move | |
645 | ;; to the end of reference found to avoid matching it twice. | |
153ae947 | 646 | (if sort-only (goto-char (nth 2 ref)) |
3ab2c837 BG |
647 | (delete-region (nth 1 ref) (nth 2 ref)) |
648 | (goto-char (nth 1 ref)) | |
271672fa | 649 | (insert (format "[%d]" marker)) |
3ab2c837 BG |
650 | (and inlinep |
651 | org-footnote-fill-after-inline-note-extraction | |
652 | (org-fill-paragraph))) | |
e66ba1df | 653 | ;; Add label (REF), identifier (MARKER), definition (DEF) |
153ae947 BG |
654 | ;; type (INLINEP) and position (POS) to REF-TABLE if data |
655 | ;; was unknown. | |
3ab2c837 | 656 | (unless a |
271672fa | 657 | (let ((def (or (nth 3 ref) ; Inline definition. |
3ab2c837 | 658 | (nth 3 (org-footnote-get-definition lbl))))) |
271672fa | 659 | (push (list lbl marker def |
153ae947 BG |
660 | ;; Reference beginning position is a marker |
661 | ;; to preserve it during further buffer | |
662 | ;; modifications. | |
663 | inlinep (copy-marker pos)) ref-table))))) | |
3ab2c837 BG |
664 | ;; 2. Find and remove the footnote section, if any. Also |
665 | ;; determine where footnotes shall be inserted (INS-POINT). | |
0bd48b37 | 666 | (cond |
8223b1d2 | 667 | ((and org-footnote-section (derived-mode-p 'org-mode)) |
153ae947 BG |
668 | (goto-char (point-min)) |
669 | (if (re-search-forward | |
2f885dca BG |
670 | (concat "^\\*[ \t]+" (regexp-quote org-footnote-section) |
671 | "[ \t]*$") nil t) | |
153ae947 BG |
672 | (delete-region (match-beginning 0) (org-end-of-subtree t t))) |
673 | ;; A new footnote section is inserted by default at the end of | |
674 | ;; the buffer. | |
e66ba1df | 675 | (goto-char (point-max)) |
153ae947 BG |
676 | (skip-chars-backward " \r\t\n") |
677 | (forward-line) | |
e66ba1df | 678 | (unless (bolp) (newline))) |
153ae947 BG |
679 | ;; No footnote section set: Footnotes will be added at the end |
680 | ;; of the section containing their first reference. | |
271672fa | 681 | ((derived-mode-p 'org-mode)) |
0bd48b37 | 682 | (t |
e66ba1df BG |
683 | ;; Remove any left-over tag in the buffer, if one is set up. |
684 | (when org-footnote-tag-for-non-org-mode-files | |
685 | (let ((tag (concat "^" (regexp-quote | |
686 | org-footnote-tag-for-non-org-mode-files) | |
687 | "[ \t]*$"))) | |
153ae947 | 688 | (goto-char (point-min)) |
e66ba1df BG |
689 | (while (re-search-forward tag nil t) |
690 | (replace-match "") | |
691 | (delete-region (point) (progn (forward-line) (point)))))) | |
692 | ;; In Message mode, ensure footnotes are inserted before the | |
3ab2c837 | 693 | ;; signature. |
e66ba1df BG |
694 | (if (and (derived-mode-p 'message-mode) |
695 | (goto-char (point-max)) | |
696 | (re-search-backward message-signature-separator nil t)) | |
697 | (beginning-of-line) | |
698 | (goto-char (point-max))))) | |
271672fa | 699 | (setq ins-point (point-marker)) |
3ab2c837 | 700 | ;; 3. Clean-up REF-TABLE. |
0bd48b37 | 701 | (setq ref-table |
3ab2c837 BG |
702 | (delq nil |
703 | (mapcar | |
0bd48b37 | 704 | (lambda (x) |
3ab2c837 BG |
705 | (cond |
706 | ;; When only sorting, ignore inline footnotes. | |
153ae947 BG |
707 | ;; Also clear position marker. |
708 | ((and sort-only (nth 3 x)) | |
709 | (set-marker (nth 4 x) nil) nil) | |
3ab2c837 BG |
710 | ;; No definition available: provide one. |
711 | ((not (nth 2 x)) | |
153ae947 BG |
712 | (append |
713 | (list (car x) (nth 1 x) | |
714 | (format "DEFINITION NOT FOUND: %s" (car x))) | |
715 | (nthcdr 3 x))) | |
3ab2c837 BG |
716 | (t x))) |
717 | ref-table))) | |
718 | (setq ref-table (nreverse ref-table)) | |
e66ba1df | 719 | ;; 4. Remove left-over definitions in the buffer. |
153ae947 BG |
720 | (mapc (lambda (x) |
721 | (unless (nth 3 x) (org-footnote-delete-definitions (car x)))) | |
e66ba1df BG |
722 | ref-table) |
723 | ;; 5. Insert the footnotes again in the buffer, at the | |
3ab2c837 | 724 | ;; appropriate spot. |
e66ba1df | 725 | (goto-char ins-point) |
3ab2c837 BG |
726 | (cond |
727 | ;; No footnote: exit. | |
728 | ((not ref-table)) | |
729 | ;; Cases when footnotes should be inserted in one place. | |
271672fa | 730 | ((or (not (derived-mode-p 'org-mode)) org-footnote-section) |
e66ba1df BG |
731 | ;; Insert again the section title, if any. Ensure that title, |
732 | ;; or the subsequent footnotes, will be separated by a blank | |
733 | ;; lines from the rest of the document. In an Org buffer, | |
734 | ;; separate section with a blank line, unless explicitly | |
735 | ;; stated in `org-blank-before-new-entry'. | |
271672fa BG |
736 | (if (not (derived-mode-p 'org-mode)) |
737 | (progn (skip-chars-backward " \t\n\r") | |
738 | (delete-region (point) ins-point) | |
739 | (unless (bolp) (newline)) | |
740 | (when org-footnote-tag-for-non-org-mode-files | |
741 | (insert "\n" org-footnote-tag-for-non-org-mode-files "\n"))) | |
e66ba1df BG |
742 | (when (and (cdr (assq 'heading org-blank-before-new-entry)) |
743 | (zerop (save-excursion (org-back-over-empty-lines)))) | |
744 | (insert "\n")) | |
271672fa | 745 | (insert "* " org-footnote-section "\n")) |
e66ba1df BG |
746 | (set-marker ins-point nil) |
747 | ;; Insert the footnotes, separated by a blank line. | |
153ae947 BG |
748 | (insert |
749 | (mapconcat | |
750 | (lambda (x) | |
751 | ;; Clean markers. | |
752 | (set-marker (nth 4 x) nil) | |
753 | (format "\n[%s] %s" (nth (if sort-only 0 1) x) (nth 2 x))) | |
754 | ref-table "\n")) | |
271672fa | 755 | (unless (eobp) (insert "\n\n"))) |
153ae947 BG |
756 | ;; Each footnote definition has to be inserted at the end of |
757 | ;; the section where its first reference belongs. | |
758 | (t | |
759 | (mapc | |
760 | (lambda (x) | |
761 | (let ((pos (nth 4 x))) | |
762 | (goto-char pos) | |
763 | ;; Clean marker. | |
764 | (set-marker pos nil)) | |
765 | (org-footnote-goto-local-insertion-point) | |
766 | (insert (format "\n[%s] %s\n" | |
767 | (if sort-only (car x) (nth 1 x)) | |
768 | (nth 2 x)))) | |
769 | ref-table)))))) | |
0bd48b37 CD |
770 | |
771 | (defun org-footnote-goto-local-insertion-point () | |
772 | "Find insertion point for footnote, just before next outline heading." | |
c8d0cf5c | 773 | (org-with-limited-levels (outline-next-heading)) |
0bd48b37 CD |
774 | (or (bolp) (newline)) |
775 | (beginning-of-line 0) | |
776 | (while (and (not (bobp)) (= (char-after) ?#)) | |
777 | (beginning-of-line 0)) | |
8223b1d2 | 778 | (if (let ((case-fold-search t)) (looking-at "[ \t]*#\\+tblfm:")) (beginning-of-line 2)) |
0bd48b37 | 779 | (end-of-line 1) |
afe98dfa CD |
780 | (skip-chars-backward "\n\r\t ") |
781 | (forward-line)) | |
0bd48b37 | 782 | |
3ab2c837 BG |
783 | (defun org-footnote-delete-references (label) |
784 | "Delete every reference to footnote LABEL. | |
785 | Return the number of footnotes removed." | |
786 | (save-excursion | |
787 | (goto-char (point-min)) | |
788 | (let (ref (nref 0)) | |
789 | (while (setq ref (org-footnote-get-next-reference label)) | |
790 | (goto-char (nth 1 ref)) | |
791 | (delete-region (nth 1 ref) (nth 2 ref)) | |
792 | (incf nref)) | |
793 | nref))) | |
794 | ||
795 | (defun org-footnote-delete-definitions (label) | |
796 | "Delete every definition of the footnote LABEL. | |
797 | Return the number of footnotes removed." | |
798 | (save-excursion | |
799 | (goto-char (point-min)) | |
800 | (let ((def-re (concat "^\\[" (regexp-quote label) "\\]")) | |
801 | (ndef 0)) | |
802 | (while (re-search-forward def-re nil t) | |
803 | (let ((full-def (org-footnote-at-definition-p))) | |
153ae947 | 804 | (when full-def |
8223b1d2 BG |
805 | ;; Remove the footnote, and all blank lines before it. |
806 | (goto-char (nth 1 full-def)) | |
807 | (skip-chars-backward " \r\t\n") | |
808 | (unless (bolp) (forward-line)) | |
809 | (delete-region (point) (nth 2 full-def)) | |
153ae947 | 810 | (incf ndef)))) |
3ab2c837 BG |
811 | ndef))) |
812 | ||
0bd48b37 CD |
813 | (defun org-footnote-delete (&optional label) |
814 | "Delete the footnote at point. | |
815 | This will remove the definition (even multiple definitions if they exist) | |
3ab2c837 BG |
816 | and all references of a footnote label. |
817 | ||
818 | If LABEL is non-nil, delete that footnote instead." | |
0bd48b37 | 819 | (catch 'done |
3ab2c837 BG |
820 | (let* ((nref 0) (ndef 0) x |
821 | ;; 1. Determine LABEL of footnote at point. | |
822 | (label (cond | |
823 | ;; LABEL is provided as argument. | |
824 | (label) | |
8223b1d2 | 825 | ;; Footnote reference at point. If the footnote is |
3ab2c837 BG |
826 | ;; anonymous, delete it and exit instead. |
827 | ((setq x (org-footnote-at-reference-p)) | |
828 | (or (car x) | |
829 | (progn | |
830 | (delete-region (nth 1 x) (nth 2 x)) | |
831 | (message "Anonymous footnote removed") | |
832 | (throw 'done t)))) | |
833 | ;; Footnote definition at point. | |
834 | ((setq x (org-footnote-at-definition-p)) | |
835 | (car x)) | |
836 | (t (error "Don't know which footnote to remove"))))) | |
837 | ;; 2. Now that LABEL is non-nil, find every reference and every | |
838 | ;; definition, and delete them. | |
839 | (setq nref (org-footnote-delete-references label) | |
840 | ndef (org-footnote-delete-definitions label)) | |
841 | ;; 3. Verify consistency of footnotes and notify user. | |
c8d0cf5c | 842 | (org-footnote-auto-adjust-maybe) |
0bd48b37 CD |
843 | (message "%d definition(s) of and %d reference(s) of footnote %s removed" |
844 | ndef nref label)))) | |
845 | ||
c8d0cf5c CD |
846 | (defun org-footnote-renumber-fn:N () |
847 | "Renumber the simple footnotes like fn:17 into a sequence in the document." | |
848 | (interactive) | |
e66ba1df BG |
849 | (let (map (n 0)) |
850 | (org-with-wide-buffer | |
851 | (goto-char (point-min)) | |
852 | (while (re-search-forward "\\[fn:\\([0-9]+\\)[]:]" nil t) | |
853 | (save-excursion | |
854 | (goto-char (match-beginning 0)) | |
855 | ;; Ensure match is a footnote reference or definition. | |
856 | (when (save-match-data (if (bolp) | |
857 | (org-footnote-at-definition-p) | |
858 | (org-footnote-at-reference-p))) | |
859 | (let ((new-val (or (cdr (assoc (match-string 1) map)) | |
860 | (number-to-string (incf n))))) | |
861 | (unless (assoc (match-string 1) map) | |
862 | (push (cons (match-string 1) new-val) map)) | |
863 | (replace-match new-val nil nil nil 1)))))))) | |
c8d0cf5c CD |
864 | |
865 | (defun org-footnote-auto-adjust-maybe () | |
866 | "Renumber and/or sort footnotes according to user settings." | |
867 | (when (memq org-footnote-auto-adjust '(t renumber)) | |
868 | (org-footnote-renumber-fn:N)) | |
869 | (when (memq org-footnote-auto-adjust '(t sort)) | |
3ab2c837 | 870 | (let ((label (car (org-footnote-at-definition-p)))) |
c8d0cf5c CD |
871 | (org-footnote-normalize 'sort) |
872 | (when label | |
873 | (goto-char (point-min)) | |
874 | (and (re-search-forward (concat "^\\[" (regexp-quote label) "\\]") | |
875 | nil t) | |
876 | (progn (insert " ") | |
877 | (just-one-space))))))) | |
878 | ||
0bd48b37 CD |
879 | (provide 'org-footnote) |
880 | ||
bdebdb64 BG |
881 | ;; Local variables: |
882 | ;; generated-autoload-file: "org-loaddefs.el" | |
883 | ;; End: | |
884 | ||
0bd48b37 | 885 | ;;; org-footnote.el ends here |