Commit | Line | Data |
---|---|---|
0bd48b37 CD |
1 | ;;; org-footnote.el --- Footnote support in Org and elsewhere |
2 | ;; | |
3 | ;; Copyright (C) 2009 Free Software Foundation, Inc. | |
4 | ;; | |
5 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
6 | ;; Keywords: outlines, hypermedia, calendar, wp | |
7 | ;; Homepage: http://orgmode.org | |
d6685abc | 8 | ;; Version: 6.20c |
0bd48b37 CD |
9 | ;; |
10 | ;; This file is part of GNU Emacs. | |
11 | ;; | |
12 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
13 | ;; it under the terms of the GNU General Public License as published by | |
14 | ;; the Free Software Foundation, either version 3 of the License, or | |
15 | ;; (at your option) any later version. | |
16 | ||
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
24 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
25 | ;; | |
26 | ;;; Commentary: | |
27 | ||
28 | ;; This file contains the code dealing with footnotes in Org-mode. | |
29 | ;; The code can also be used in arbitrary text modes to provide | |
30 | ;; footnotes. Compared to Steven L Baur's footnote.el it provides | |
31 | ;; better support for resuming editing. It is less configurable than | |
32 | ;; Steve's code, though. | |
33 | ||
34 | ;;; Code: | |
35 | ||
36 | (eval-when-compile | |
37 | (require 'cl)) | |
38 | (require 'org-macs) | |
39 | (require 'org-compat) | |
40 | ||
41 | (declare-function org-in-regexp "org" (re &optional nlines visually)) | |
42 | (declare-function org-mark-ring-push "org" (&optional pos buffer)) | |
43 | (declare-function outline-next-heading "outline") | |
44 | (declare-function org-trim "org" (s)) | |
45 | (declare-function org-show-context "org" (&optional key)) | |
46 | (declare-function org-back-to-heading "org" (&optional invisible-ok)) | |
47 | (declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading)) | |
48 | ||
49 | (defconst org-footnote-re | |
50 | (concat "[^][\n]" ; to make sure it is not at the beginning of a line | |
51 | "\\[" | |
52 | "\\(?:" | |
53 | "\\([0-9]+\\)" | |
54 | "\\|" | |
55 | (org-re "\\(fn:\\([-_[:word:]]+?\\)?\\)\\(?::\\([^\]]*?\\)\\)?") | |
56 | "\\)" | |
57 | "\\]") | |
58 | "Regular expression for matching footnotes.") | |
59 | ||
60 | (defconst org-footnote-definition-re | |
61 | (org-re "^\\(\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]\\)") | |
62 | "Regular expression matching the definition of a footnote.") | |
63 | ||
64 | (defcustom org-footnote-section "Footnotes" | |
65 | "Outline heading containing footnote definitions before export. | |
66 | This can be nil, to place footnotes locally at the end of the current | |
67 | outline node. If can also be the name of a special outline heading | |
68 | under which footnotes should be put. | |
69 | This variable defines the place where Org puts the definition | |
70 | automatically, i.e. when creating the footnote, and when sorting the notes. | |
71 | However, by hand you may place definitions *anywhere*. | |
72 | If this is a string, during export, all subtrees starting with this | |
73 | heading will be removed after extracting footnote definitions." | |
74 | :group 'org-footnotes | |
75 | :type '(choice | |
76 | (string :tag "Collect fotnotes under heading") | |
77 | (const :tag "Define footnotes locally" nil))) | |
78 | ||
79 | (defcustom org-footnote-tag-for-non-org-mode-files "Footnotes:" | |
80 | "Tag marking the beginning of footnote section. | |
81 | The Org-mode footnote engine can be used in arbitrary text files as well | |
82 | as in Org-mode. Outside Org-mode, new footnotes are always placed at | |
83 | the end of the file. When you normalize the notes, any line containing | |
84 | only this tag will be removed, a new one will be inserted at the end | |
85 | of the file, followed by the collected and normalized footnotes." | |
86 | :group 'org-footnotes | |
87 | :type 'string) | |
88 | ||
89 | (defcustom org-footnote-define-inline nil | |
90 | "Non-nil means, define footnotes inline, at reference location. | |
91 | When nil, footnotes will be defined in a special section near | |
92 | the end of the document. When t, the [fn:label:definition] notation | |
93 | will be used to define the footnote at the reference position." | |
94 | :group 'org-footnote | |
95 | :type 'boolean) | |
96 | ||
97 | (defcustom org-footnote-auto-label t | |
98 | "Non-nil means, define automatically new labels for footnotes. | |
99 | Possible values are: | |
100 | ||
101 | nil prompt the user for each label | |
102 | t create unique labels of the form [fn:1], [fn:2], ... | |
103 | confirm like t, but let the user edit the created value. In particular, | |
104 | the label can be removed from the minibuffer, to create | |
105 | an anonymous footnote. | |
106 | plain Automatically create plain number labels like [1]" | |
107 | :group 'org-footnote | |
108 | :type '(choice | |
109 | (const :tag "Frompt for label" nil) | |
110 | (const :tag "Create automatic [fn:N]" t) | |
111 | (const :tag "Offer automatic [fn:N] for editing" confirm) | |
112 | (const :tag "Create automatic [N]" plain))) | |
113 | ||
114 | (defcustom org-footnote-fill-after-inline-note-extraction nil | |
115 | "Non-nil means, fill paragraphs after extracting footnotes. | |
116 | When extracting inline footnotes, the lengths of lines can change a lot. | |
117 | When this option is set, paragraphs from which an inline footnote has been | |
118 | extracted will be filled again." | |
119 | :group 'org-footnote | |
120 | :type 'boolean) | |
121 | ||
122 | (defun org-footnote-at-reference-p () | |
123 | "Is the cursor at a footnote reference? | |
124 | If yes, return the beginning position, the label, and the definition, if local." | |
125 | (when (org-in-regexp org-footnote-re 15) | |
126 | (list (match-beginning 0) | |
127 | (or (match-string 1) | |
128 | (if (equal (match-string 2) "fn:") nil (match-string 2))) | |
129 | (match-string 4)))) | |
130 | ||
131 | (defun org-footnote-at-definition-p () | |
132 | "Is the cursor at a footnote definition. | |
133 | This matches only pure definitions like [1] or [fn:name] at the beginning | |
134 | of a line. It does not a references like [fn:name:definition], where the | |
135 | footnote text is included and defined locally. | |
136 | The return value will be nil if not at a foornote definition, and a list | |
137 | with start and label of the footnote if there is a definition at point." | |
138 | (save-excursion | |
139 | (end-of-line 1) | |
140 | (let ((lim (save-excursion (re-search-backward "^\\*+ \\|^[ \t]*$" nil t)))) | |
141 | (when (re-search-backward org-footnote-definition-re lim t) | |
142 | (list (match-beginning 0) (match-string 2)))))) | |
143 | ||
144 | (defun org-footnote-goto-definition (label) | |
145 | "Find the definition of the footnote with label LABEL." | |
146 | (interactive "sLabel: ") | |
147 | (org-mark-ring-push) | |
148 | (setq label (org-footnote-normalize-label label)) | |
149 | (let ((re (format "^\\[%s\\]\\|.\\[%s:" label label)) | |
150 | pos) | |
151 | (save-excursion | |
152 | (setq pos (or (re-search-forward re nil t) | |
153 | (and (goto-char (point-min)) | |
154 | (re-search-forward re nil t)) | |
155 | (and (progn (widen) t) | |
156 | (goto-char (point-min)) | |
157 | (re-search-forward re nil t))))) | |
158 | (if (not pos) | |
159 | (error "Cannot find definition of footnote %s" label) | |
160 | (goto-char pos) | |
161 | (org-show-context 'link-search) | |
162 | (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'.")))) | |
163 | ||
164 | (defun org-footnote-goto-next-reference (label) | |
165 | "Find the definition of the footnote with label LABEL." | |
166 | (interactive "sLabel: ") | |
167 | (org-mark-ring-push) | |
168 | (setq label (org-footnote-normalize-label label)) | |
169 | (let ((re (format ".\\[%s[]:]" label)) | |
170 | (p0 (point)) pos) | |
171 | (save-excursion | |
172 | (setq pos (or (re-search-forward re nil t) | |
173 | (and (goto-char (point-min)) | |
174 | (re-search-forward re nil t)) | |
175 | (and (progn (widen) t) | |
176 | (goto-char p0) | |
177 | (re-search-forward re nil t)) | |
178 | (and (goto-char (point-min)) | |
179 | (re-search-forward re nil t))))) | |
180 | (if pos | |
181 | (progn | |
182 | (goto-char pos) | |
183 | (org-show-context 'link-search)) | |
184 | (error "Cannot find reference of footnote %s" label)))) | |
185 | ||
186 | (defun org-footnote-normalize-label (label) | |
187 | (if (numberp label) (setq label (number-to-string label))) | |
188 | (if (not (string-match "^[0-9]+$\\|^$\\|^fn:" label)) | |
189 | (setq label (concat "fn:" label))) | |
190 | label) | |
191 | ||
192 | (defun org-footnote-all-labels () | |
193 | "Return list with all defined foot labels used in the buffer." | |
194 | (let (rtn l) | |
195 | (save-excursion | |
196 | (save-restriction | |
197 | (widen) | |
198 | (goto-char (point-min)) | |
199 | (while (re-search-forward org-footnote-definition-re nil t) | |
200 | (setq l (org-match-string-no-properties 2)) | |
201 | (and l (add-to-list 'rtn l))) | |
202 | (goto-char (point-min)) | |
203 | (while (re-search-forward org-footnote-re nil t) | |
204 | (setq l (or (org-match-string-no-properties 1) | |
205 | (org-match-string-no-properties 2))) | |
206 | (and l (not (equal l "fn:")) (add-to-list 'rtn l))))) | |
207 | rtn)) | |
208 | ||
209 | (defun org-footnote-unique-label (&optional current) | |
210 | "Return a new unique footnote label. | |
211 | The returns the firsts fn:N labels that is currently not used." | |
212 | (unless current (setq current (org-footnote-all-labels))) | |
213 | (let ((fmt (if (eq org-footnote-auto-label 'plain) "%d" "fn:%d")) | |
214 | (cnt 1)) | |
215 | (while (member (format fmt cnt) current) | |
216 | (incf cnt)) | |
217 | (format fmt cnt))) | |
218 | ||
219 | (defvar org-footnote-label-history nil | |
220 | "History of footnote labels entered in current buffer.") | |
221 | (make-variable-buffer-local 'org-footnote-label-history) | |
222 | ||
223 | (defun org-footnote-new () | |
224 | "Insert a new footnote. | |
225 | This command prompts for a label. If this is a label referencing an | |
226 | existing label, only insert the label. If the footnote label is empty | |
227 | or new, let the user edit the definition of the footnote." | |
228 | (interactive) | |
229 | (let* ((labels (org-footnote-all-labels)) | |
230 | (propose (org-footnote-unique-label labels)) | |
231 | (label | |
232 | (if (member org-footnote-auto-label '(t plain)) | |
233 | propose | |
234 | (completing-read | |
235 | "Label (leave empty for anonymous): " | |
236 | (mapcar 'list labels) nil nil | |
237 | (if (eq org-footnote-auto-label 'confirm) propose nil) | |
238 | 'org-footnote-label-history)))) | |
239 | (setq label (org-footnote-normalize-label label)) | |
240 | (cond | |
241 | ((equal label "") | |
242 | (insert "[fn:: ]") | |
243 | (backward-char 1)) | |
244 | ((member label labels) | |
245 | (insert "[" label "]") | |
246 | (message "New reference to existing note")) | |
247 | (org-footnote-define-inline | |
248 | (insert "[" label ": ]") | |
249 | (backward-char 1)) | |
250 | (t | |
251 | (insert "[" label "]") | |
252 | (org-footnote-create-definition label))))) | |
253 | ||
254 | (defun org-footnote-create-definition (label) | |
255 | "Start the definition of a footnote with label LABEL." | |
256 | (interactive "sLabel: ") | |
257 | (setq label (org-footnote-normalize-label label)) | |
65c439fd | 258 | (let (re) |
0bd48b37 CD |
259 | (cond |
260 | ((org-mode-p) | |
261 | (if (not org-footnote-section) | |
262 | ;; No section, put footnote into the current outline node | |
263 | nil | |
264 | ;; Try to find or make the special node | |
265 | (setq re (concat "^\\*+[ \t]+" org-footnote-section "[ \t]*$")) | |
266 | (unless (or (re-search-forward re nil t) | |
267 | (and (progn (widen) t) | |
268 | (re-search-forward re nil t))) | |
269 | (goto-char (point-max)) | |
270 | (insert "\n\n* " org-footnote-section "\n"))) | |
271 | ;; Now go to the end of this entry and insert there. | |
272 | (org-footnote-goto-local-insertion-point)) | |
273 | (t | |
274 | (setq re (concat "^" org-footnote-tag-for-non-org-mode-files "[ \t]*$")) | |
275 | (unless (re-search-forward re nil t) | |
276 | (goto-char (point-max)) | |
277 | (skip-chars-backward " \t\r\n") | |
278 | (insert "\n\n") | |
279 | (delete-region (point) (point-max)) | |
280 | (insert org-footnote-tag-for-non-org-mode-files "\n")) | |
281 | (goto-char (point-max)) | |
282 | (skip-chars-backward " \t\r\n"))) | |
283 | (insert "\n\n") | |
284 | (insert "[" label "] ") | |
285 | (message "Edit definition and go back with `C-c &' or, if unique, with `C-c C-c'."))) | |
286 | ||
287 | ;;;###autoload | |
288 | (defun org-footnote-action (&optional special) | |
289 | "Do the right thing for footnotes. | |
290 | When at a foornote reference, jump to the definition. When at a definition, | |
291 | jump to the refernces. When neither at definition or reference, | |
292 | create a new footnote, interactively. | |
293 | With prefix arg SPECIAL, offer additional commands in a menu." | |
294 | (interactive "P") | |
295 | (let (tmp c) | |
296 | (cond | |
297 | (special | |
298 | (message "Footnotes: [s]ort | convert to [n]umeric | [d]elete") | |
299 | (setq c (read-char-exclusive)) | |
300 | (cond | |
301 | ((equal c ?s) | |
302 | (org-footnote-normalize 'sort)) | |
303 | ((equal c ?n) | |
304 | (org-footnote-normalize)) | |
305 | ((equal c ?d) | |
306 | (org-footnote-delete)) | |
307 | (t (error "No such footnote command %c" c)))) | |
308 | ((setq tmp (org-footnote-at-reference-p)) | |
309 | (if (nth 1 tmp) | |
310 | (org-footnote-goto-definition (nth 1 tmp)) | |
311 | (goto-char (match-beginning 4)))) | |
312 | ((setq tmp (org-footnote-at-definition-p)) | |
313 | (org-footnote-goto-next-reference (nth 1 tmp))) | |
314 | (t (org-footnote-new))))) | |
315 | ||
316 | ;;;###autoload | |
317 | (defun org-footnote-normalize (&optional sort-only for-preprocessor) | |
318 | "Collect the footnotes in various formats and normalize them. | |
319 | This find the different sorts of footnotes allowed in Org, and | |
320 | normalizes them to the usual [N] format that is understood by the | |
321 | Org-mode exporters. | |
322 | When SORT-ONLY is set, only sort the footnote definitions into the | |
323 | referenced sequence." | |
324 | ;; This is based on Paul's function, but rewritten. | |
65c439fd | 325 | (let ((count 0) ref def idef ref-table beg beg1 marker a before |
0bd48b37 CD |
326 | ins-point) |
327 | (save-excursion | |
328 | ;; Now find footnote references, and extract the definitions | |
329 | (goto-char (point-min)) | |
330 | (while (re-search-forward org-footnote-re nil t) | |
331 | (org-if-unprotected | |
332 | (setq def (match-string 4) | |
333 | idef def | |
334 | ref (or (match-string 1) (match-string 2)) | |
335 | before (char-to-string (char-after (match-beginning 0)))) | |
336 | (if (equal ref "fn:") (setq ref nil)) | |
337 | (if (and ref (setq a (assoc ref ref-table))) | |
338 | (progn | |
339 | (setq marker (nth 1 a)) | |
340 | (unless (nth 2 a) (setf (caddr a) def))) | |
341 | (setq marker (number-to-string (incf count)))) | |
342 | (save-match-data | |
343 | (if def | |
344 | (setq def (org-trim def)) | |
345 | (save-excursion | |
346 | (goto-char (point-min)) | |
347 | (if (not (re-search-forward (concat "^\\[" (regexp-quote ref) | |
348 | "\\]") nil t)) | |
349 | (setq def nil) | |
350 | (setq beg (match-beginning 0)) | |
351 | (setq beg1 (match-end 0)) | |
352 | (re-search-forward | |
353 | (org-re "^[ \t]*$\\|^\\*+ \\|^\\[\\([0-9]+\\|fn:[-_[:word:]]+\\)\\]") | |
354 | nil 'move) | |
355 | (setq def (buffer-substring beg1 (or (match-beginning 0) | |
356 | (point-max)))) | |
357 | (goto-char beg) | |
358 | (skip-chars-backward " \t\n\t") | |
359 | (delete-region (1+ (point)) (match-beginning 0)))))) | |
360 | (unless sort-only | |
361 | (replace-match (concat before "[" marker "]")) | |
362 | (and idef | |
363 | org-footnote-fill-after-inline-note-extraction | |
364 | (fill-paragraph))) | |
365 | (if (not a) (push (list ref marker def) ref-table)))) | |
366 | ||
367 | ;; First find and remove the footnote section | |
368 | (goto-char (point-min)) | |
369 | (cond | |
370 | ((org-mode-p) | |
371 | (if (and org-footnote-section | |
372 | (re-search-forward | |
373 | (concat "^\\*[ \t]+" (regexp-quote org-footnote-section) | |
374 | "[ \t]*$") | |
375 | nil t)) | |
376 | (if (or for-preprocessor (not org-footnote-section)) | |
377 | (replace-match "") | |
378 | (org-back-to-heading t) | |
379 | (forward-line 1) | |
380 | (setq ins-point (point)) | |
381 | (delete-region (point) (org-end-of-subtree t))) | |
382 | (goto-char (point-max)) | |
383 | (unless for-preprocessor | |
384 | (when org-footnote-section | |
385 | (or (bolp) (insert "\n")) | |
386 | (insert "* " org-footnote-section "\n") | |
387 | (setq ins-point (point)))))) | |
388 | (t | |
389 | (if (re-search-forward | |
390 | (concat "^" | |
391 | (regexp-quote org-footnote-tag-for-non-org-mode-files) | |
392 | "[ \t]*$") | |
393 | nil t) | |
394 | (replace-match "")) | |
395 | (goto-char (point-max)) | |
396 | (skip-chars-backward " \t\n\r") | |
397 | (delete-region (point) (point-max)) | |
398 | (insert "\n\n" org-footnote-tag-for-non-org-mode-files "\n") | |
399 | (setq ins-point (point)))) | |
400 | ||
401 | ;; Insert the footnotes again | |
402 | (goto-char (or ins-point (point-max))) | |
403 | (setq ref-table (reverse ref-table)) | |
404 | (when sort-only | |
405 | ;; remove anonymous fotnotes from the list | |
406 | (setq ref-table | |
407 | (delq nil (mapcar | |
408 | (lambda (x) (and (car x) | |
409 | (not (equal (car x) "fn:")) | |
410 | x)) | |
411 | ref-table)))) | |
412 | ;; Make sure each footnote has a description, or an error message. | |
413 | (setq ref-table | |
414 | (mapcar | |
415 | (lambda (x) | |
416 | (if (not (nth 2 x)) | |
417 | (setcar (cddr x) | |
418 | (format "FOOTNOTE DEFINITION NOT FOUND: %s" (car x))) | |
419 | (setcar (cddr x) (org-trim (nth 2 x)))) | |
420 | x) | |
421 | ref-table)) | |
422 | ||
423 | (if (or (not (org-mode-p)) ; not an Org file | |
424 | org-footnote-section ; we do not use a footnote section | |
425 | (not sort-only) ; this is normalization | |
426 | for-preprocessor) ; the is the preprocessor | |
427 | ;; Insert the footnotes together in one place | |
428 | (progn | |
429 | (setq def | |
430 | (mapconcat | |
431 | (lambda (x) | |
432 | (format "[%s] %s" (nth (if sort-only 0 1) x) | |
433 | (org-trim (nth 2 x)))) | |
434 | ref-table "\n\n")) | |
435 | (if ref-table (insert "\n" def "\n\n"))) | |
436 | ;; Insert each footnote near the first reference | |
437 | ;; Happens only in Org files with no special footnote section, | |
438 | ;; and only when doing sorting | |
439 | (mapc 'org-insert-footnote-reference-near-definition | |
440 | ref-table))))) | |
441 | ||
442 | (defun org-insert-footnote-reference-near-definition (entry) | |
443 | "Find first reference of footnote ENTRY and insert the definition there. | |
444 | ENTRY is (fn-label num-mark definition)." | |
445 | (when (car entry) | |
65c439fd CD |
446 | (goto-char (point-min)) |
447 | (when (re-search-forward (format ".\\[%s[]:]" (regexp-quote (car entry))) | |
448 | nil t) | |
449 | (org-footnote-goto-local-insertion-point) | |
450 | (insert (format "\n\n[%s] %s" (car entry) (nth 2 entry)))))) | |
0bd48b37 CD |
451 | |
452 | (defun org-footnote-goto-local-insertion-point () | |
453 | "Find insertion point for footnote, just before next outline heading." | |
454 | (outline-next-heading) | |
455 | (or (bolp) (newline)) | |
456 | (beginning-of-line 0) | |
457 | (while (and (not (bobp)) (= (char-after) ?#)) | |
458 | (beginning-of-line 0)) | |
459 | (if (looking-at "#\\+TBLFM:") (beginning-of-line 2)) | |
460 | (end-of-line 1) | |
461 | (skip-chars-backward "\n\r\t ")) | |
462 | ||
463 | (defun org-footnote-delete (&optional label) | |
464 | "Delete the footnote at point. | |
465 | This will remove the definition (even multiple definitions if they exist) | |
466 | and all references of a footnote label." | |
467 | (catch 'done | |
468 | (let (x label l beg def-re (nref 0) (ndef 0)) | |
469 | (unless label | |
470 | (when (setq x (org-footnote-at-reference-p)) | |
471 | (setq label (nth 1 x)) | |
472 | (when (or (not label) (equal "fn:" label)) | |
473 | (delete-region (1+ (match-beginning 0)) (match-end 0)) | |
474 | (message "Anonymous footnote removed") | |
475 | (throw 'done t))) | |
476 | (when (and (not label) (setq x (org-footnote-at-definition-p))) | |
477 | (setq label (nth 1 x))) | |
478 | (unless label (error "Don't know which footnote to remove"))) | |
479 | (save-excursion | |
480 | (save-restriction | |
481 | (goto-char (point-min)) | |
482 | (while (re-search-forward org-footnote-re nil t) | |
483 | (setq l (or (match-string 1) (match-string 2))) | |
484 | (when (equal l label) | |
485 | (delete-region (1+ (match-beginning 0)) (match-end 0)) | |
486 | (incf nref))) | |
487 | (goto-char (point-min)) | |
488 | (setq def-re (concat "^\\[" (regexp-quote label) "\\]")) | |
489 | (while (re-search-forward def-re nil t) | |
490 | (setq beg (match-beginning 0)) | |
491 | (if (re-search-forward "^\\[\\|^[ \t]*$\\|^\\*+ " nil t) | |
492 | (goto-char (match-beginning 0)) | |
493 | (goto-char (point-max))) | |
494 | (delete-region beg (point)) | |
495 | (incf ndef)))) | |
496 | (message "%d definition(s) of and %d reference(s) of footnote %s removed" | |
497 | ndef nref label)))) | |
498 | ||
499 | (provide 'org-footnote) | |
500 | ||
501 | ;; arch-tag: 1b5954df-fb5d-4da5-8709-78d944dbfc37 | |
502 | ||
503 | ;;; org-footnote.el ends here |