summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2012-06-06 23:39:04 +0200
committerNicolas Goaziou <n.goaziou@gmail.com>2012-06-06 23:39:04 +0200
commitad235400a6e311891b012d60c366ade60ba3dc81 (patch)
tree40f8a9b98620116027674dbb7371736fe63c8a9c
parent343a705ba51afa426ab60820ea6a16d82bffb999 (diff)
downloadorg-mode-ad235400a6e311891b012d60c366ade60ba3dc81.tar.gz
org-export: Support for external id links
* contrib/lisp/org-export.el (org-export-get-buffer-attributes): Retrieve footnote definitions and id in buffer. (org-export-store-footnote-definitions): Removed function. (org-export-collect-tree-properties): Update docstring. (org-export-as): Do not call `org-export-store-footnote-definitions'. (org-export-resolve-id-link): Return external file name when there's no match for id in current parse tree. * contrib/lisp/org-e-latex.el (org-e-latex-link): Handle external id links. * testing/lisp/test-org-export.el: Add tests.
-rw-r--r--contrib/lisp/org-e-latex.el4
-rw-r--r--contrib/lisp/org-export.el106
-rw-r--r--testing/lisp/test-org-export.el271
3 files changed, 194 insertions, 187 deletions
diff --git a/contrib/lisp/org-e-latex.el b/contrib/lisp/org-e-latex.el
index 67e9197..043b75c 100644
--- a/contrib/lisp/org-e-latex.el
+++ b/contrib/lisp/org-e-latex.el
@@ -1598,6 +1598,10 @@ INFO is a plist holding contextual information. See
(org-export-resolve-fuzzy-link link info)
(org-export-resolve-id-link link info))))
(case (org-element-type destination)
+ ;; Id link points to an external file.
+ (plain-text
+ (if desc (format "\\href{file://%s}{%s}" destination desc)
+ (format "\\url{file://%s}" destination)))
;; Fuzzy link points nowhere.
('nil
(format org-e-latex-link-with-unknown-path-format
diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el
index b9294e5..f81a9f6 100644
--- a/contrib/lisp/org-export.el
+++ b/contrib/lisp/org-export.el
@@ -1065,7 +1065,7 @@ inferior to file-local settings."
(and buffer-file-name (org-remove-double-quotes buffer-file-name)))
;; ... and from subtree, when appropriate.
(and subtreep (org-export-get-subtree-options))
- ;; Also install back-end symbol and its translation table.
+ ;; Eventually install back-end symbol and its translation table.
`(:back-end
,backend
:translate-alist
@@ -1266,6 +1266,40 @@ Assume buffer is in Org mode. Narrowing, if any, is ignored."
(file-name-sans-extension
(file-name-nondirectory visited-file)))
(buffer-name (buffer-base-buffer)))
+ :footnote-definition-alist
+ ;; Footnotes definitions must be collected in the original
+ ;; buffer, as there's no insurance that they will still be in the
+ ;; parse tree, due to possible narrowing.
+ (let (alist)
+ (org-with-wide-buffer
+ (goto-char (point-min))
+ (while (re-search-forward org-footnote-definition-re nil t)
+ (let ((def (org-footnote-at-definition-p)))
+ (when def
+ (org-skip-whitespace)
+ (push (cons (car def)
+ (save-restriction
+ (narrow-to-region (point) (nth 2 def))
+ ;; Like `org-element-parse-buffer', but
+ ;; makes sure the definition doesn't start
+ ;; with a section element.
+ (nconc
+ (list 'org-data nil)
+ (org-element-parse-elements
+ (point-min) (point-max) nil nil nil nil nil))))
+ alist))))
+ alist))
+ :id-alist
+ ;; Collect id references.
+ (let (alist)
+ (org-with-wide-buffer
+ (goto-char (point-min))
+ (while (re-search-forward
+ "\\[\\[id:\\(\\S-+?\\)\\]\\(?:\\[.*?\\]\\)?\\]" nil t)
+ (let* ((id (org-match-string-no-properties 1))
+ (file (org-id-find-id-file id)))
+ (when file (push (cons id (file-relative-name file)) alist)))))
+ alist)
:macro-modification-time
(and visited-file
(file-exists-p visited-file)
@@ -1301,47 +1335,6 @@ process."
;; Return value.
plist))
-(defun org-export-store-footnote-definitions (info)
- "Collect and store footnote definitions from current buffer in INFO.
-
-INFO is a plist containing export options.
-
-Footnotes definitions are stored as a alist whose CAR is
-footnote's label, as a string, and CDR the contents, as a parse
-tree. This alist will be consed to the value of
-`:footnote-definition-alist' in INFO, if any.
-
-The new plist is returned; use
-
- \(setq info (org-export-store-footnote-definitions info))
-
-to be sure to use the new value. INFO is modified by side
-effects."
- ;; Footnotes definitions must be collected in the original buffer,
- ;; as there's no insurance that they will still be in the parse
- ;; tree, due to some narrowing.
- (plist-put
- info :footnote-definition-alist
- (let ((alist (plist-get info :footnote-definition-alist)))
- (org-with-wide-buffer
- (goto-char (point-min))
- (while (re-search-forward org-footnote-definition-re nil t)
- (let ((def (org-footnote-at-definition-p)))
- (when def
- (org-skip-whitespace)
- (push (cons (car def)
- (save-restriction
- (narrow-to-region (point) (nth 2 def))
- ;; Like `org-element-parse-buffer', but
- ;; makes sure the definition doesn't start
- ;; with a section element.
- (nconc
- (list 'org-data nil)
- (org-element-parse-elements
- (point-min) (point-max) nil nil nil nil nil))))
- alist))))
- alist))))
-
(defvar org-export-allow-BIND-local nil)
(defun org-export-confirm-letbind ()
"Can we use #+BIND values during export?
@@ -1405,7 +1398,9 @@ Following tree properties are set or updated:
`:ignore-list' List of elements that should be ignored during
export.
-`:target-list' List of all targets in the parse tree."
+`:target-list' List of all targets in the parse tree.
+
+Return updated plist."
;; Install the parse tree in the communication channel, in order to
;; use `org-export-get-genealogy' and al.
(setq info (plist-put info :parse-tree data))
@@ -2267,8 +2262,7 @@ Return code as a string."
;; they might not be accessible anymore in a narrowed parse
;; tree. Also install user's and developer's filters.
(let ((info (org-export-install-filters
- (org-export-store-footnote-definitions
- (org-export-get-environment backend subtreep ext-plist))))
+ (org-export-get-environment backend subtreep ext-plist)))
;; 2. Get parse tree. Buffer isn't parsed directly.
;; Instead, a temporary copy is created, where include
;; keywords are expanded and code blocks are evaluated.
@@ -3007,16 +3001,20 @@ Assume LINK type is \"fuzzy\"."
INFO is a plist used as a communication channel.
-Return value can be an headline element or nil. Assume LINK type
-is either \"id\" or \"custom-id\"."
+Return value can be the headline element matched in current parse
+tree, a file name or nil. Assume LINK type is either \"id\" or
+\"custom-id\"."
(let ((id (org-element-property :path link)))
- (org-element-map
- (plist-get info :parse-tree) 'headline
- (lambda (headline)
- (when (or (string= (org-element-property :id headline) id)
- (string= (org-element-property :custom-id headline) id))
- headline))
- info 'first-match)))
+ ;; First check if id is within the current parse tree.
+ (or (org-element-map
+ (plist-get info :parse-tree) 'headline
+ (lambda (headline)
+ (when (or (string= (org-element-property :id headline) id)
+ (string= (org-element-property :custom-id headline) id))
+ headline))
+ info 'first-match)
+ ;; Otherwise, look for external files.
+ (cdr (assoc id (plist-get info :id-alist))))))
(defun org-export-resolve-radio-link (link info)
"Return radio-target object referenced as LINK destination.
diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el
index 9aedbba..8ed47fb 100644
--- a/testing/lisp/test-org-export.el
+++ b/testing/lisp/test-org-export.el
@@ -375,62 +375,56 @@ body\n")))
(ert-deftest test-org-export/footnotes ()
"Test footnotes specifications."
- (let ((org-footnote-section nil))
+ (let ((org-footnote-section nil)
+ (org-export-with-footnotes t))
;; 1. Read every type of footnote.
- (org-test-with-temp-text
+ (org-test-with-parsed-data
"Text[fn:1] [1] [fn:label:C] [fn::D]\n\n[fn:1] A\n\n[1] B"
- (let* ((tree (org-element-parse-buffer))
- (info (org-export-store-footnote-definitions
- `(:parse-tree ,tree :with-footnotes t))))
- (should
- (equal
- '((1 . "A") (2 . "B") (3 . "C") (4 . "D"))
- (org-element-map
- tree 'footnote-reference
- (lambda (ref)
- (let ((def (org-export-get-footnote-definition ref info)))
- (cons (org-export-get-footnote-number ref info)
- (if (eq (org-element-property :type ref) 'inline) (car def)
- (car (org-element-contents
- (car (org-element-contents def))))))))
- info)))))
+ (should
+ (equal
+ '((1 . "A") (2 . "B") (3 . "C") (4 . "D"))
+ (org-element-map
+ tree 'footnote-reference
+ (lambda (ref)
+ (let ((def (org-export-get-footnote-definition ref info)))
+ (cons (org-export-get-footnote-number ref info)
+ (if (eq (org-element-property :type ref) 'inline) (car def)
+ (car (org-element-contents
+ (car (org-element-contents def))))))))
+ info))))
;; 2. Test nested footnotes order.
- (org-test-with-temp-text
+ (org-test-with-parsed-data
"Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
- (let* ((tree (org-element-parse-buffer))
- (info (org-export-store-footnote-definitions
- `(:parse-tree ,tree :with-footnotes t))))
- (should
- (equal
- '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
- (org-element-map
- tree 'footnote-reference
- (lambda (ref)
- (when (org-export-footnote-first-reference-p ref info)
- (cons (org-export-get-footnote-number ref info)
- (org-element-property :label ref))))
- info)))))
+ (should
+ (equal
+ '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
+ (org-element-map
+ tree 'footnote-reference
+ (lambda (ref)
+ (when (org-export-footnote-first-reference-p ref info)
+ (cons (org-export-get-footnote-number ref info)
+ (org-element-property :label ref))))
+ info))))
;; 3. Test nested footnote in invisible definitions.
(org-test-with-temp-text "Text[1]\n\n[1] B [2]\n\n[2] C."
;; Hide definitions.
(narrow-to-region (point) (point-at-eol))
(let* ((tree (org-element-parse-buffer))
- (info (org-export-store-footnote-definitions
- `(:parse-tree ,tree :with-footnotes t))))
+ (info (org-combine-plists
+ `(:parse-tree ,tree)
+ (org-export-collect-tree-properties
+ tree (org-export-get-environment)))))
;; Both footnotes should be seen.
(should
(= (length (org-export-collect-footnote-definitions tree info)) 2))))
;; 4. Test footnotes definitions collection.
- (org-test-with-temp-text "Text[fn:1:A[fn:2]] [fn:3].
+ (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
\[fn:2] B [fn:3] [fn::D].
\[fn:3] C."
- (let* ((tree (org-element-parse-buffer))
- (info (org-export-store-footnote-definitions
- `(:parse-tree ,tree :with-footnotes t))))
- (should (= (length (org-export-collect-footnote-definitions tree info))
- 4))))
+ (should (= (length (org-export-collect-footnote-definitions tree info))
+ 4)))
;; 5. Test export of footnotes defined outside parsing scope.
(org-test-with-temp-text "[fn:1] Out of scope
* Title
@@ -484,8 +478,73 @@ Paragraph[fn:1]"
;;; Links
-(ert-deftest test-org-export/fuzzy-links ()
- "Test fuzzy link export specifications."
+(ert-deftest test-org-export/resolve-coderef ()
+ "Test `org-export-resolve-coderef' specifications."
+ (let ((org-coderef-label-format "(ref:%s)"))
+ ;; 1. A link to a "-n -k -r" block returns line number.
+ (org-test-with-parsed-data
+ "#+BEGIN_EXAMPLE -n -k -r\nText (ref:coderef)\n#+END_EXAMPLE"
+ (should (= (org-export-resolve-coderef "coderef" info) 1)))
+ (org-test-with-parsed-data
+ "#+BEGIN_SRC emacs-lisp -n -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (should (= (org-export-resolve-coderef "coderef" info) 1)))
+ ;; 2. A link to a "-n -r" block returns line number.
+ (org-test-with-parsed-data
+ "#+BEGIN_EXAMPLE -n -r\nText (ref:coderef)\n#+END_EXAMPLE"
+ (should (= (org-export-resolve-coderef "coderef" info) 1)))
+ (org-test-with-parsed-data
+ "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (should (= (org-export-resolve-coderef "coderef" info) 1)))
+ ;; 3. A link to a "-n" block returns coderef.
+ (org-test-with-parsed-data
+ "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
+ (org-test-with-parsed-data
+ "#+BEGIN_EXAMPLE -n\nText (ref:coderef)\n#+END_EXAMPLE"
+ (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
+ ;; 4. A link to a "-r" block returns line number.
+ (org-test-with-parsed-data
+ "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (should (= (org-export-resolve-coderef "coderef" info) 1)))
+ (org-test-with-parsed-data
+ "#+BEGIN_EXAMPLE -r\nText (ref:coderef)\n#+END_EXAMPLE"
+ (should (= (org-export-resolve-coderef "coderef" info) 1)))
+ ;; 5. A link to a block without a switch returns coderef.
+ (org-test-with-parsed-data
+ "#+BEGIN_SRC emacs-lisp\n(+ 1 1) (ref:coderef)\n#+END_SRC"
+ (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
+ (org-test-with-parsed-data
+ "#+BEGIN_EXAMPLE\nText (ref:coderef)\n#+END_EXAMPLE"
+ (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
+ ;; 6. Correctly handle continued line numbers. A "+n" switch
+ ;; should resume numbering from previous block with numbered
+ ;; lines, ignoring blocks not numbering lines in the process.
+ ;; A "-n" switch resets count.
+ (org-test-with-parsed-data "
+#+BEGIN_EXAMPLE -n
+Text.
+#+END_EXAMPLE
+
+#+BEGIN_SRC emacs-lisp
+\(- 1 1)
+#+END_SRC
+
+#+BEGIN_SRC emacs-lisp +n -r
+\(+ 1 1) (ref:addition)
+#+END_SRC
+
+#+BEGIN_EXAMPLE -n -r
+Another text. (ref:text)
+#+END_EXAMPLE"
+ (should (= (org-export-resolve-coderef "addition" info) 2))
+ (should (= (org-export-resolve-coderef "text" info) 1)))
+ ;; 7. Recognize coderef with user-specified syntax.
+ (org-test-with-parsed-data
+ "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText. [ref:text]\n#+END_EXAMPLE"
+ (should (equal (org-export-resolve-coderef "text" info) "text")))))
+
+(ert-deftest test-org-export/resolve-fuzzy-link ()
+ "Test `org-export-resolve-fuzzy-link' specifications."
;; 1. Links to invisible (keyword) targets should be ignored.
(org-test-with-parsed-data
"Paragraph.\n#+TARGET: Test\n[[Test]]"
@@ -551,98 +610,44 @@ Paragraph[1][2][fn:lbl3:C<<target>>][[test]][[target]]\n[1] A\n\n[2] <<test>>B"
(org-export-get-ordinal
(org-export-resolve-fuzzy-link link info) info)) info t)))))
-(ert-deftest test-org-export/resolve-coderef ()
- "Test `org-export-resolve-coderef' specifications."
- (let ((org-coderef-label-format "(ref:%s)"))
- ;; 1. A link to a "-n -k -r" block returns line number.
- (org-test-with-temp-text
- "#+BEGIN_EXAMPLE -n -k -r\nText (ref:coderef)\n#+END_EXAMPLE"
- (let ((tree (org-element-parse-buffer)))
- (should
- (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
- (org-test-with-temp-text
- "#+BEGIN_SRC emacs-lisp -n -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
- (let ((tree (org-element-parse-buffer)))
- (should
- (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
- ;; 2. A link to a "-n -r" block returns line number.
- (org-test-with-temp-text
- "#+BEGIN_EXAMPLE -n -r\nText (ref:coderef)\n#+END_EXAMPLE"
- (let ((tree (org-element-parse-buffer)))
- (should
- (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
- (org-test-with-temp-text
- "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
- (let ((tree (org-element-parse-buffer)))
- (should
- (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
- ;; 3. A link to a "-n" block returns coderef.
- (org-test-with-temp-text
- "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1) (ref:coderef)\n#+END_SRC"
- (let ((tree (org-element-parse-buffer)))
- (should
- (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
- "coderef"))))
- (org-test-with-temp-text
- "#+BEGIN_EXAMPLE -n\nText (ref:coderef)\n#+END_EXAMPLE"
- (let ((tree (org-element-parse-buffer)))
- (should
- (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
- "coderef"))))
- ;; 4. A link to a "-r" block returns line number.
- (org-test-with-temp-text
- "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
- (let ((tree (org-element-parse-buffer)))
- (should
- (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
- (org-test-with-temp-text
- "#+BEGIN_EXAMPLE -r\nText (ref:coderef)\n#+END_EXAMPLE"
- (let ((tree (org-element-parse-buffer)))
- (should
- (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
- ;; 5. A link to a block without a switch returns coderef.
- (org-test-with-temp-text
- "#+BEGIN_SRC emacs-lisp\n(+ 1 1) (ref:coderef)\n#+END_SRC"
- (let ((tree (org-element-parse-buffer)))
- (should
- (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
- "coderef"))))
- (org-test-with-temp-text
- "#+BEGIN_EXAMPLE\nText (ref:coderef)\n#+END_EXAMPLE"
- (let ((tree (org-element-parse-buffer)))
- (should
- (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
- "coderef"))))
- ;; 6. Correctly handle continued line numbers. A "+n" switch
- ;; should resume numbering from previous block with numbered
- ;; lines, ignoring blocks not numbering lines in the process.
- ;; A "-n" switch resets count.
- (org-test-with-temp-text "
-#+BEGIN_EXAMPLE -n
-Text.
-#+END_EXAMPLE
-
-#+BEGIN_SRC emacs-lisp
-\(- 1 1)
-#+END_SRC
-
-#+BEGIN_SRC emacs-lisp +n -r
-\(+ 1 1) (ref:addition)
-#+END_SRC
-
-#+BEGIN_EXAMPLE -n -r
-Another text. (ref:text)
-#+END_EXAMPLE"
- (let* ((tree (org-element-parse-buffer))
- (info `(:parse-tree ,tree)))
- (should (= (org-export-resolve-coderef "addition" info) 2))
- (should (= (org-export-resolve-coderef "text" info) 1))))
- ;; 7. Recognize coderef with user-specified syntax.
- (org-test-with-temp-text
- "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText. [ref:text]\n#+END_EXAMPLE"
- (let ((tree (org-element-parse-buffer)))
- (should (equal (org-export-resolve-coderef "text" `(:parse-tree ,tree))
- "text"))))))
+(ert-deftest test-org-export/resolve-id-link ()
+ "Test `org-export-resolve-id-link' specifications."
+ ;; 1. Regular test for custom-id link.
+ (org-test-with-parsed-data "* Headline1
+:PROPERTIES:
+:CUSTOM-ID: test
+:END:
+* Headline 2
+\[[#test]]"
+ (should
+ (org-export-resolve-id-link
+ (org-element-map tree 'link 'identity info t) info)))
+ ;; 2. Failing test for custom-id link.
+ (org-test-with-parsed-data "* Headline1
+:PROPERTIES:
+:CUSTOM-ID: test
+:END:
+* Headline 2
+\[[#no-match]]"
+ (should-not
+ (org-export-resolve-id-link
+ (org-element-map tree 'link 'identity info t) info)))
+ ;; 3. Test for internal id target.
+ (org-test-with-parsed-data "* Headline1
+:PROPERTIES:
+:ID: aaaa
+:END:
+* Headline 2
+\[[id:aaaa]]"
+ (should
+ (org-export-resolve-id-link
+ (org-element-map tree 'link 'identity info t) info)))
+ ;; 4. Test for external id target.
+ (org-test-with-parsed-data "[[id:aaaa]]"
+ (should
+ (org-export-resolve-id-link
+ (org-element-map tree 'link 'identity info t)
+ (org-combine-plists info '(:id-alist (("aaaa" . "external-file"))))))))
(ert-deftest test-org-export/resolve-radio-link ()
"Test `org-export-resolve-radio-link' specifications."