From 451191bc41d5a05f65714fa2b1340f20fa88baed Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Thu, 7 Jun 2012 00:06:19 +0200 Subject: [PATCH] org-export: Correctly resolve fuzzy links whose path starts with * * contrib/lisp/org-export.el (org-export-resolve-fuzzy-link): Correctly resolve fuzzy links whose path starts with * * testing/lisp/test-org-export.el: Add tests. --- contrib/lisp/org-export.el | 55 +++++----- testing/lisp/test-org-export.el | 180 +++++++++++++++++++++----------- 2 files changed, 146 insertions(+), 89 deletions(-) diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el index f81a9f665..9434cfe81 100644 --- a/contrib/lisp/org-export.el +++ b/contrib/lisp/org-export.el @@ -2949,18 +2949,19 @@ Return value can be an object, an element, or nil: - Otherwise, return nil. Assume LINK type is \"fuzzy\"." - (let ((path (org-element-property :path link))) + (let* ((path (org-element-property :path link)) + (match-title-p (eq (aref path 0) ?*))) (cond ;; First try to find a matching "<>" unless user specified ;; he was looking for an headline (path starts with a * ;; character). - ((and (not (eq (substring path 0 1) ?*)) + ((and (not match-title-p) (loop for target in (plist-get info :target-list) when (string= (org-element-property :value target) path) return target))) ;; Then try to find an element with a matching "#+NAME: path" ;; affiliated keyword. - ((and (not (eq (substring path 0 1) ?*)) + ((and (not match-title-p) (org-element-map (plist-get info :parse-tree) org-element-all-elements (lambda (el) @@ -2972,29 +2973,31 @@ Assume LINK type is \"fuzzy\"." ;; is found, return it, otherwise return nil. (t (let ((find-headline - (function - ;; Return first headline whose `:raw-value' property - ;; is NAME in parse tree DATA, or nil. - (lambda (name data) - (org-element-map - data 'headline - (lambda (headline) - (when (string= - (org-element-property :raw-value headline) - name) - headline)) - info 'first-match))))) - ;; Search among headlines sharing an ancestor with link, - ;; from closest to farthest. - (or (catch 'exit - (mapc - (lambda (parent) - (when (eq (org-element-type parent) 'headline) - (let ((foundp (funcall find-headline path parent))) - (when foundp (throw 'exit foundp))))) - (org-export-get-genealogy link info)) nil) - ;; No match with a common ancestor: try the full parse-tree. - (funcall find-headline path (plist-get info :parse-tree)))))))) + (function + ;; Return first headline whose `:raw-value' property + ;; is NAME in parse tree DATA, or nil. + (lambda (name data) + (org-element-map + data 'headline + (lambda (headline) + (when (string= + (org-element-property :raw-value headline) + name) + headline)) + info 'first-match))))) + ;; Search among headlines sharing an ancestor with link, + ;; from closest to farthest. + (or (catch 'exit + (mapc + (lambda (parent) + (when (eq (org-element-type parent) 'headline) + (let ((foundp (funcall find-headline path parent))) + (when foundp (throw 'exit foundp))))) + (org-export-get-genealogy link info)) nil) + ;; No match with a common ancestor: try the full parse-tree. + (funcall find-headline + (if match-title-p (substring path 1) path) + (plist-get info :parse-tree)))))))) (defun org-export-resolve-id-link (link info) "Return headline referenced as LINK destination. diff --git a/testing/lisp/test-org-export.el b/testing/lisp/test-org-export.el index 8ed47fb90..86453d751 100644 --- a/testing/lisp/test-org-export.el +++ b/testing/lisp/test-org-export.el @@ -478,6 +478,73 @@ Paragraph[fn:1]" ;;; Links +(ert-deftest test-org-export/fuzzy-link () + "Test fuzzy links specifications." + ;; 1. Links to invisible (keyword) targets should be ignored. + (org-test-with-parsed-data + "Paragraph.\n#+TARGET: Test\n[[Test]]" + (should-not + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info))) + ;; 2. Link to an headline should return headline's number. + (org-test-with-parsed-data + "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]" + (should + ;; Note: Headline's number is in fact a list of numbers. + (equal '(2) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t)))) + ;; 3. Link to a target in an item should return item's number. + (org-test-with-parsed-data + "- Item1\n - Item11\n - <>Item12\n- Item2\n\n\n[[test]]" + (should + ;; Note: Item's number is in fact a list of numbers. + (equal '(1 2) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t)))) + ;; 4. Link to a target in a footnote should return footnote's + ;; number. + (org-test-with-parsed-data " +Paragraph[1][2][fn:lbl3:C<>][[test]][[target]]\n[1] A\n\n[2] <>B" + (should + (equal '(2 3) + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info)))) + ;; 5. Link to a named element should return sequence number of that + ;; element. + (org-test-with-parsed-data + "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]" + (should + (= 2 + (org-element-map + tree 'link + (lambda (link) + (org-export-get-ordinal + (org-export-resolve-fuzzy-link link info) info)) info t)))) + ;; 6. Link to a target not within an item, a table, a footnote + ;; reference or definition should return section number. + (org-test-with-parsed-data + "* Head1\n* Head2\nParagraph<>\n* Head3\n[[target]]" + (should + (equal '(2) + (org-element-map + tree 'link + (lambda (link) + (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)")) @@ -543,72 +610,59 @@ Another text. (ref:text) "#+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 () +(ert-deftest test-org-exprot/resolve-fuzzy-link () "Test `org-export-resolve-fuzzy-link' specifications." - ;; 1. Links to invisible (keyword) targets should be ignored. + ;; 1. Match target objects. + (org-test-with-parsed-data "<> [[target]]" + (should + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))) + ;; 2. Match target elements. + (org-test-with-parsed-data "#+TARGET: target\n[[target]]" + (should + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))) + ;; 3. Match named elements. + (org-test-with-parsed-data "#+NAME: target\nParagraph\n\n[[target]]" + (should + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))) + ;; 4. Match exact headline's name. + (org-test-with-parsed-data "* My headline\n[[My headline]]" + (should + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))) + ;; 5. Targets objects have priority over named elements and headline + ;; titles. (org-test-with-parsed-data - "Paragraph.\n#+TARGET: Test\n[[Test]]" + "* target\n#+NAME: target\n<>\n\n[[target]]" + (should + (eq 'target + (org-element-type + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))))) + ;; 6. Named elements have priority over headline titles. + (org-test-with-parsed-data + "* target\n#+NAME: target\nParagraph\n\n[[target]]" + (should + (eq 'paragraph + (org-element-type + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))))) + ;; 7. If link's path starts with a "*", only match headline titles, + ;; though. + (org-test-with-parsed-data + "* target\n#+NAME: target\n<>\n\n[[*target]]" + (should + (eq 'headline + (org-element-type + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info))))) + ;; 8. Return nil if no match. + (org-test-with-parsed-data "[[target]]" (should-not - (org-element-map - tree 'link - (lambda (link) - (org-export-get-ordinal - (org-export-resolve-fuzzy-link link info) info)) info))) - ;; 2. Link to an headline should return headline's number. - (org-test-with-parsed-data - "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]" - (should - ;; Note: Headline's number is in fact a list of numbers. - (equal '(2) - (org-element-map - tree 'link - (lambda (link) - (org-export-get-ordinal - (org-export-resolve-fuzzy-link link info) info)) info t)))) - ;; 3. Link to a target in an item should return item's number. - (org-test-with-parsed-data - "- Item1\n - Item11\n - <>Item12\n- Item2\n\n\n[[test]]" - (should - ;; Note: Item's number is in fact a list of numbers. - (equal '(1 2) - (org-element-map - tree 'link - (lambda (link) - (org-export-get-ordinal - (org-export-resolve-fuzzy-link link info) info)) info t)))) - ;; 4. Link to a target in a footnote should return footnote's - ;; number. - (org-test-with-parsed-data " -Paragraph[1][2][fn:lbl3:C<>][[test]][[target]]\n[1] A\n\n[2] <>B" - (should - (equal '(2 3) - (org-element-map - tree 'link - (lambda (link) - (org-export-get-ordinal - (org-export-resolve-fuzzy-link link info) info)) info)))) - ;; 5. Link to a named element should return sequence number of that - ;; element. - (org-test-with-parsed-data - "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]" - (should - (= 2 - (org-element-map - tree 'link - (lambda (link) - (org-export-get-ordinal - (org-export-resolve-fuzzy-link link info) info)) info t)))) - ;; 6. Link to a target not within an item, a table, a footnote - ;; reference or definition should return section number. - (org-test-with-parsed-data - "* Head1\n* Head2\nParagraph<>\n* Head3\n[[target]]" - (should - (equal '(2) - (org-element-map - tree 'link - (lambda (link) - (org-export-get-ordinal - (org-export-resolve-fuzzy-link link info) info)) info t))))) + (org-export-resolve-fuzzy-link + (org-element-map tree 'link 'identity info t) info)))) (ert-deftest test-org-export/resolve-id-link () "Test `org-export-resolve-id-link' specifications."