diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el index 7414dbf6f..849eae0fd 100644 --- a/contrib/lisp/org-element.el +++ b/contrib/lisp/org-element.el @@ -273,20 +273,27 @@ CONTENTS is the contents of the element." (defun org-element-footnote-definition-parser () "Parse a footnote definition. -Return a list whose car is `footnote-definition' and cdr is +Return a list whose CAR is `footnote-definition' and CDR is a plist containing `:label', `:begin' `:end', `:contents-begin', -`:contents-end' and `:post-blank' keywords." +`:contents-end' and `:post-blank' keywords. + +Assume point is at the beginning of the footnote definition." (save-excursion - (let* ((f-def (org-footnote-at-definition-p)) - (label (car f-def)) - (keywords (progn (goto-char (nth 1 f-def)) - (org-element-collect-affiliated-keywords))) + (looking-at org-footnote-definition-re) + (let* ((label (org-match-string-no-properties 1)) + (keywords (org-element-collect-affiliated-keywords)) (begin (car keywords)) - (contents-begin (progn (looking-at (concat "\\[" label "\\]")) - (goto-char (match-end 0)) + (contents-begin (progn (search-forward "]") (org-skip-whitespace) (point))) - (contents-end (goto-char (nth 2 f-def))) + (contents-end (if (progn + (end-of-line) + (re-search-forward + (concat org-outline-regexp-bol "\\|" + org-footnote-definition-re "\\|" + "^[ \t]*$") nil t)) + (match-beginning 0) + (point-max))) (end (progn (org-skip-whitespace) (if (eobp) (point) (point-at-bol))))) `(footnote-definition @@ -1783,28 +1790,37 @@ its beginning position." (defun org-element-footnote-reference-parser () "Parse footnote reference at point. -Return a list whose car is `footnote-reference' and cdr a plist +Return a list whose CAR is `footnote-reference' and CDR a plist with `:label', `:type', `:inline-definition', `:begin', `:end' and `:post-blank' as keywords." (save-excursion - (let* ((ref (org-footnote-at-reference-p)) - (label (car ref)) - (inline-def - (let ((raw-def (nth 3 ref))) - (and raw-def - (org-element-parse-secondary-string - raw-def - (cdr (assq 'footnote-reference - org-element-string-restrictions)))))) - (type (if (nth 3 ref) 'inline 'standard)) - (begin (nth 1 ref)) - (post-blank (progn (goto-char (nth 2 ref)) + (looking-at org-footnote-re) + (let* ((begin (point)) + (label (or (org-match-string-no-properties 2) + (org-match-string-no-properties 3) + (and (match-string 1) + (concat "fn:" (org-match-string-no-properties 1))))) + (type (if (or (not label) (match-string 1)) 'inline 'standard)) + (inner-begin (match-end 0)) + (inner-end + (let ((count 1)) + (forward-char) + (while (and (> count 0) (re-search-forward "[][]" nil t)) + (if (equal (match-string 0) "[") (incf count) (decf count))) + (1- (point)))) + (post-blank (progn (goto-char (1+ inner-end)) (skip-chars-forward " \t"))) - (end (point))) + (end (point)) + (inline-definition + (and (eq type 'inline) + (org-element-parse-secondary-string + (buffer-substring inner-begin inner-end) + (cdr (assq 'footnote-reference + org-element-string-restrictions)))))) `(footnote-reference (:label ,label :type ,type - :inline-definition ,inline-def + :inline-definition ,inline-definition :begin ,begin :end ,end :post-blank ,post-blank))))) @@ -1825,11 +1841,19 @@ CONTENTS is nil." LIMIT bounds the search. -Return value is a cons cell whose car is `footnote-reference' and -cdr is beginning position." - (let (fn-ref) - (when (setq fn-ref (org-footnote-get-next-reference nil nil limit)) - (cons 'footnote-reference (nth 1 fn-ref))))) +Return value is a cons cell whose CAR is `footnote-reference' and +CDR is beginning position." + (save-excursion + (catch 'exit + (while (re-search-forward org-footnote-re limit t) + (save-excursion + (let ((beg (match-beginning 0)) + (count 1)) + (backward-char) + (while (re-search-forward "[][]" limit t) + (if (equal (match-string 0) "[") (incf count) (decf count)) + (when (zerop count) + (throw 'exit (cons 'footnote-reference beg)))))))))) ;;;; Inline Babel Call diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index 385cc6b32..040f35d7c 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -203,6 +203,63 @@ (equal (org-element-property :label-fmt element) "[ref:%s]")))))) + +;;;; Footnotes references and definitions + +(ert-deftest test-org-element/footnote-reference () + "Test footnote-reference parsing." + ;; 1. Parse a standard reference. + (org-test-with-temp-text "[fn:label]" + (should (equal (org-element-footnote-reference-parser) + '(footnote-reference + (:label "fn:label" :type standard :inline-definition nil + :begin 1 :end 11 :post-blank 0))))) + ;; 2. Parse a normalized reference. + (org-test-with-temp-text "[1]" + (should (equal (org-element-footnote-reference-parser) + '(footnote-reference + (:label "1" :type standard :inline-definition nil + :begin 1 :end 4 :post-blank 0))))) + ;; 3. Parse an inline reference. + (org-test-with-temp-text "[fn:test:def]" + (should (equal (org-element-footnote-reference-parser) + '(footnote-reference + (:label "fn:test" :type inline :inline-definition ("def") + :begin 1 :end 14 :post-blank 0))))) + ;; 4. Parse an anonymous reference. + (org-test-with-temp-text "[fn::def]" + (should (equal (org-element-footnote-reference-parser) + '(footnote-reference + (:label nil :type inline :inline-definition ("def") + :begin 1 :end 10 :post-blank 0))))) + ;; 5. Parse nested footnotes. + (org-test-with-temp-text "[fn::def [fn:label]]" + (should + (equal + (org-element-footnote-reference-parser) + '(footnote-reference + (:label nil :type inline + :inline-definition + ("def " + (footnote-reference + (:label "fn:label" :type standard :inline-definition nil + :begin 5 :end 15 :post-blank 0))) + :begin 1 :end 21 :post-blank 0))))) + ;; 6. Parse adjacent footnotes. + (org-test-with-temp-text "[fn:label1][fn:label2]" + (should + (equal + (org-element-footnote-reference-parser) + '(footnote-reference + (:label "fn:label1" :type standard :inline-definition nil :begin 1 + :end 12 :post-blank 0))))) + ;; 7. Only properly closed footnotes are recognized as such. + (org-test-with-temp-text "Text [fn:label" + (should-not + (org-element-map + (org-element-parse-buffer) 'footnote-reference 'identity)))) + + ;;; Granularity