emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [O] [dev] Implement "ref" link types


From: Nicolas Goaziou
Subject: Re: [O] [dev] Implement "ref" link types
Date: Mon, 27 Feb 2012 20:38:46 +0100

Hello,

Here is a new version of the patch built on top of master, along with
test cases.

If there is no objection, I'll push it to master in a couple of days.
I really think that's a great feature to have in Org.


Regards,

-- 
Nicolas Goaziou
>From 2fdde87bb7f1241f3d24dbd8ae030a300fe8f0fc Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <address@hidden>
Date: Mon, 20 Feb 2012 22:24:38 +0100
Subject: [PATCH] Implement numbered cross-references

* lisp/org.el (org-link-search): Search for #+name affiliated keywords
  and invisible targets.
* contrib/lisp/org-element.el (org-element-link-parser): Remove "ref"
  links relative part.
(org-element-target-parser): Move property name from `:raw-value' to
`:value'.
(org-element-recursive-objects): Remove targets from tables.  Cells
are not parsed unless explicitely asked by back-end developer, too
late.  A target wouldn't be noticed in time.  One solution could be to
parse every table, but that's time consumming.
(org-element-object-restrictions): Target are not recursive anymore.
* contrib/lisp/org-export.el (org-export-resolve-fuzzy-link): Find
  elements with a matching "#+name: path" affiliated keyword.
(org-export-get-ordinal): Make special cases for headlines, items,
footnotes definitions and references.
(org-export-resolve-ref-link): Removed function.
* EXPERIMENTAL/org-e-latex.el (org-e-latex-link): Handle
  cross-reference numbers.
(org-e-latex-target): Targets have no contents.
* EXPERIMENTAL/org-e-ascii.el (org-e-ascii--describe-links): Ignore
  fuzzy links in link description at the end of the section.
(org-e-ascii-link): Handle cross-reference numbers.
* testing/contrib/lisp/test-org-export.el: Add tests.
* testing/lisp/test-org.el: Add tests.
---
 EXPERIMENTAL/org-e-ascii.el             |   80 ++++++++----------
 EXPERIMENTAL/org-e-latex.el             |   50 +++++------
 contrib/lisp/org-element.el             |   20 ++---
 contrib/lisp/org-export.el              |  141 ++++++++++++++++++++-----------
 lisp/org.el                             |   16 ++++
 testing/contrib/lisp/test-org-export.el |   91 ++++++++++++++++++++
 testing/lisp/test-org.el                |   41 +++++++++
 7 files changed, 299 insertions(+), 140 deletions(-)

diff --git a/EXPERIMENTAL/org-e-ascii.el b/EXPERIMENTAL/org-e-ascii.el
index 0eb547b..c9cca4a 100644
--- a/EXPERIMENTAL/org-e-ascii.el
+++ b/EXPERIMENTAL/org-e-ascii.el
@@ -825,28 +825,24 @@ channel."
                     (if (not desc) (org-element-property :raw-link link)
                       (org-export-secondary-string desc 'e-ascii info)))))
        (cond
-       ;; Coderefs, radio links and ref links are ignored.
-       ((member type '("coderef" "radio" "ref")) nil)
-       ;; Id, custom-id and fuzzy links (with the exception of
-       ;; targets): Headlines refer to their numbering.
-       ((member type '("custom-id" "fuzzy" "id"))
-        (let ((destination (if (string= type "fuzzy")
-                               (org-export-resolve-fuzzy-link link info)
-                             (org-export-resolve-id-link link info))))
-          (unless (eq (org-element-type destination) 'target)
-            (concat
-             (org-e-ascii--fill-string
-              (format
-               "[%s] %s"
-               anchor
-               (if (not destination)
-                   (org-e-ascii--translate "Unknown reference" info)
-                 (format
-                  (org-e-ascii--translate "See section %s" info)
-                  (mapconcat 'number-to-string
-                             (org-export-get-headline-number destination info)
-                             "."))))
-              width info) "\n\n"))))
+       ;; Coderefs, radio links and fuzzy links are ignored.
+       ((member type '("coderef" "radio" "fuzzy")) nil)
+       ;; Id and custom-id links: Headlines refer to their numbering.
+       ((member type '("custom-id" "id"))
+        (let ((destination (org-export-resolve-id-link link info)))
+          (concat
+           (org-e-ascii--fill-string
+            (format
+             "[%s] %s"
+             anchor
+             (if (not destination)
+                 (org-e-ascii--translate "Unknown reference" info)
+               (format
+                (org-e-ascii--translate "See section %s" info)
+                (mapconcat 'number-to-string
+                           (org-export-get-headline-number destination info)
+                           "."))))
+            width info) "\n\n")))
        ;; Do not add a link that cannot be resolved and doesn't have
        ;; any description: destination is already visible in the
        ;; paragraph.
@@ -1390,29 +1386,23 @@ INFO is a plist holding contextual information."
        (org-element-property :path link)
        (cdr (assq 'radio-target org-element-object-restrictions)))
        'e-ascii info))
-     ;; Ref link: If there's no description (DESC, return link's
-     ;; destination sequence number among elements of same
-     ;; type. Otherwise, use DESC.
-     ((string= type "ref")
-      (if (org-string-nw-p desc) desc
-       (format "%d"
-               (org-export-get-ordinal
-                (org-export-resolve-ref-link link info)
-                info nil nil
-                (lambda (el) (or (org-element-property :caption el)
-                            (org-element-property :name el)))))))
      ;; Do not apply a special syntax on fuzzy links pointing to
      ;; targets.
-     ((and (string= type "fuzzy")
-          (let ((path (org-element-property :path link)))
-            (loop for target in (plist-get info :target-list)
-                  thereis (string=
-                           (org-element-property :raw-value target)
-                           path))))
-      (if (org-string-nw-p desc) desc raw-link))
+     ((string= type "fuzzy")
+      (let ((destination (org-export-resolve-fuzzy-link link info)))
+       ;; Ignore invisible "#+target: path".
+       (unless (eq (org-element-type destination) 'keyword)
+         (if (org-string-nw-p desc) desc
+           (when destination
+             (let ((number (org-export-get-ordinal destination info)))
+               (when number
+                 (if (atom number) (number-to-string number)
+                   (mapconcat 'number-to-string number ".")))))))))
      (t
-      (concat (format "[%s]" (if (org-string-nw-p desc) desc raw-link))
-             (unless org-e-ascii-links-to-notes (format " (%s)" raw-link)))))))
+      (if (not (org-string-nw-p desc)) (format "[%s]" raw-link)
+       (concat
+        (format "[%s]" desc)
+        (unless org-e-ascii-links-to-notes (format " (%s)" raw-link))))))))
 
 
 ;;;; Macro
@@ -1850,11 +1840,7 @@ INFO is a plist used as a communication channel."
 
 ;;;; Target
 
-(defun org-e-ascii-target (target contents info)
-  "Transcode a TARGET object from Org to ASCII.
-CONTENTS is the contents of the target.  INFO is a plist holding
-contextual information."
-  contents)
+;; Targets are invisible.
 
 
 ;;;; Time-stamp
diff --git a/EXPERIMENTAL/org-e-latex.el b/EXPERIMENTAL/org-e-latex.el
index 43bbde7..01b8ee2 100644
--- a/EXPERIMENTAL/org-e-latex.el
+++ b/EXPERIMENTAL/org-e-latex.el
@@ -1291,8 +1291,8 @@ CONTENTS is nil.  INFO is a plist holding contextual 
information."
     (cond
      ((string= key "latex") value)
      ((string= key "index") (format "\\index{%s}" value))
-     ((string= key "target")
-      (format "\\label{%s}" (org-export-solidify-link-text value)))
+     ;; Invisible targets.
+     ((string= key "target") nil)
      ((string= key "toc")
       (let ((value (downcase value)))
        (cond
@@ -1451,32 +1451,26 @@ INFO is a plist holding contextual information.  See
               (org-element-parse-secondary-string
                path (cdr (assq 'radio-target org-element-object-restrictions)))
               'e-latex info)))
-     ;; Ref link: If no description is provided, reference label PATH
-     ;; and display table number.  Otherwise move to label but display
-     ;; description instead.
-     ((string= type "ref")
-      (if (not desc) (format "\\ref{%s}" path)
-       (format "\\hyperref[%s]{%s}" path desc)))
      ;; Links pointing to an headline: Find destination and build
      ;; appropriate referencing command.
      ((member type '("custom-id" "fuzzy" "id"))
       (let ((destination (if (string= type "fuzzy")
                             (org-export-resolve-fuzzy-link link info)
                           (org-export-resolve-id-link link info))))
-       ;; Fuzzy link points to a target.  Do as above.
        (case (org-element-type destination)
-         (target
-          (format "\\hyperref[%s]{%s}"
-                  (org-export-solidify-link-text
-                   (org-element-property :raw-value destination))
+         ;; Fuzzy link points nowhere.
+         ('nil
+          (format "\\texttt{%s}"
                   (or desc
                       (org-export-secondary-string
                        (org-element-property :raw-link link)
                        'e-latex info))))
-         ;; Fuzzy link points to an headline.  If headlines are
-         ;; numbered and the link has no description, display
-         ;; headline's number.  Otherwise, display description or
-         ;; headline's title.
+         ;; Fuzzy link points to an invisible target.
+         (keyword nil)
+         ;; LINK points to an headline.  If headlines are numbered
+         ;; and the link has no description, display headline's
+         ;; number.  Otherwise, display description or headline's
+         ;; title.
          (headline
           (let ((label
                  (format "sec-%s"
@@ -1491,13 +1485,11 @@ INFO is a plist holding contextual information.  See
                           (org-export-secondary-string
                            (org-element-property :title destination)
                            'e-latex info))))))
-         ;; Fuzzy link points nowhere.
+          ;; Fuzzy link points to a target.  Do as above.
          (otherwise
-          (format "\\texttt{%s}"
-                  (or desc
-                      (org-export-secondary-string
-                       (org-element-property :raw-link link)
-                       'e-latex info)))))))
+          (let ((path (org-export-solidify-link-text path)))
+            (if (not desc) (format "\\ref{%s}" path)
+              (format "\\hyperref[%s]{%s}" path desc)))))))
      ;; Coderef: replace link with the reference name or the
      ;; equivalent line number.
      ((string= type "coderef")
@@ -1970,14 +1962,12 @@ CONTENTS is nil.  INFO is a plist holding contextual 
information."
 
 ;;;; Target
 
-(defun org-e-latex-target (target text info)
+(defun org-e-latex-target (target contents info)
   "Transcode a TARGET object from Org to LaTeX.
-TEXT is the text of the target.  INFO is a plist holding
-contextual information."
-  (format "\\label{%s}%s"
-         (org-export-solidify-link-text
-          (org-element-property :raw-value target))
-         text))
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (format "\\label{%s}"
+         (org-export-solidify-link-text (org-element-property :value target))))
 
 
 ;;;; Time-stamp
diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el
index 4e5e7fd..de5d4ea 100644
--- a/contrib/lisp/org-element.el
+++ b/contrib/lisp/org-element.el
@@ -1967,9 +1967,6 @@ Assume point is at the beginning of the link."
         ;; Explicit type (http, irc, bbdb...).  See `org-link-types'.
         ((string-match org-link-re-with-space3 link)
          (setq type (match-string 1 link) path (match-string 2 link)))
-        ;; Ref type: PATH is the name of the target element.
-        ((string-match "^ref:\\(.*\\)" link)
-         (setq type "ref" path (org-trim (match-string 1 link))))
         ;; Id type: PATH is the id.
         ((string-match "^id:\\([-a-f0-9]+\\)" link)
          (setq type "id" path (match-string 1 link)))
@@ -2269,25 +2266,21 @@ CONTENTS is the contents of the object."
   "Parse target at point.
 
 Return a list whose car is `target' and cdr a plist with
-`:begin', `:end', `:contents-begin', `:contents-end', `raw-value'
-and `:post-blank' as keywords.
+`:begin', `:end', `:contents-begin', `:contents-end', `value' and
+`:post-blank' as keywords.
 
 Assume point is at the target."
   (save-excursion
     (looking-at org-target-regexp)
     (let ((begin (point))
-         (contents-begin (match-beginning 1))
-         (contents-end (match-end 1))
-         (raw-value (org-match-string-no-properties 1))
+         (value (org-match-string-no-properties 1))
          (post-blank (progn (goto-char (match-end 0))
                             (skip-chars-forward " \t")))
          (end (point)))
       `(target
        (:begin ,begin
                :end ,end
-               :contents-begin ,contents-begin
-               :contents-end ,contents-end
-               :raw-value ,raw-value
+               :value ,value
                :post-blank ,post-blank)))))
 
 (defun org-element-target-interpreter (target contents)
@@ -2481,7 +2474,7 @@ regexp matching one object can also match the other 
object.")
   "Complete list of object types.")
 
 (defconst org-element-recursive-objects
-  '(emphasis link macro subscript superscript target radio-target)
+  '(emphasis link macro subscript superscript radio-target)
   "List of recursive object types.")
 
 (defconst org-element-non-recursive-block-alist
@@ -2551,8 +2544,7 @@ This list is checked after translations have been 
applied.  See
     (subscript entity export-snippet inline-babel-call inline-src-block
               latex-fragment sub/superscript text-markup)
     (superscript entity export-snippet inline-babel-call inline-src-block
-                latex-fragment sub/superscript text-markup)
-    (target entity export-snippet latex-fragment sub/superscript text-markup))
+                latex-fragment sub/superscript text-markup))
   "Alist of recursive objects restrictions.
 
 CAR is a recursive object type and CDR is a list of successors
diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el
index b809758..e7cca50 100644
--- a/contrib/lisp/org-export.el
+++ b/contrib/lisp/org-export.el
@@ -1289,7 +1289,13 @@ Following tree properties are set:
    `(:parse-tree
      ,data
      :target-list
-     ,(org-element-map data 'target 'identity info)
+     ,(org-element-map
+       data '(keyword target)
+       (lambda (blob)
+        (when (or (eq (org-element-type blob) 'target)
+                  (string= (upcase (org-element-property :key blob))
+                            "TARGET"))
+          blob)) info)
      :headline-numbering ,(org-export-collect-headline-numbering data info)
      :back-end ,backend)
    info))
@@ -2697,8 +2703,11 @@ INFO is a plist holding contextual information.
 
 Return value can be an object, an element, or nil:
 
-- If LINK path exactly matches any target, return the target
-  object.
+- If LINK path matches a target object (i.e. <<path>>) or
+  element (i.e. \"#+target: path\"), return it.
+
+- If LINK path exactly matches the name affiliated keyword
+  \(i.e. #+name: path) of an element, return that element.
 
 - If LINK path exactly matches any headline name, return that
   element.  If more than one headline share that name, priority
@@ -2709,16 +2718,29 @@ Return value can be an object, an element, or nil:
 
 Assume LINK type is \"fuzzy\"."
   (let ((path (org-element-property :path link)))
-    ;; Link points to a target: return it.
-    (or (loop for target in (plist-get info :target-list)
-             when (string= (org-element-property :raw-value target) path)
-             return target)
-       ;; Link either points to an headline or nothing.  Try to find
-       ;; the source, with priority given to headlines with the closest
-       ;; common ancestor.  If such candidate is found, return its
-       ;; beginning position as an unique identifier, otherwise return
-       ;; nil.
-       (let ((find-headline
+    (cond
+     ;; First try to find a matching "<<path>>" unless user specified
+     ;; he was looking for an headline (path starts with a *
+     ;; character).
+     ((and (not (eq (substring path 0 1) ?*))
+          (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) ?*))
+          (org-element-map
+           (plist-get info :parse-tree) org-element-all-elements
+           (lambda (el)
+              (when (string= (org-element-property :name el) path) el))
+           info 'first-match)))
+     ;; Last case: link either points to an headline or to
+     ;; nothingness.  Try to find the source, with priority given to
+     ;; headlines with the closest common ancestor.  If such candidate
+     ;; is found, return its beginning position as an unique
+     ;; identifier, otherwise return nil.
+     (t
+      (let ((find-headline
               (function
                ;; Return first headline whose `:raw-value' property
                ;; is NAME in parse tree DATA, or nil.
@@ -2741,7 +2763,7 @@ Assume LINK type is \"fuzzy\"."
                       (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)))))))
+             (funcall find-headline path (plist-get info :parse-tree))))))))
 
 (defun org-export-resolve-id-link (link info)
   "Return headline referenced as LINK destination.
@@ -2759,20 +2781,6 @@ is either \"id\" or \"custom-id\"."
          headline))
      info 'first-match)))
 
-(defun org-export-resolve-ref-link (link info)
-  "Return element referenced as LINK destination.
-
-INFO is a plist used as a communication channel.
-
-Assume LINK type is \"ref\" and.  Return value is the first
-element whose `:name' property matches LINK's `:path', or nil."
-  (let ((name (org-element-property :path link)))
-    (org-element-map
-     (plist-get info :parse-tree) org-element-all-elements
-     (lambda (el)
-       (when (string= (org-element-property :name el) name) el))
-     info 'first-match)))
-
 (defun org-export-resolve-coderef (ref info)
   "Resolve a code reference REF.
 
@@ -2863,27 +2871,62 @@ Optional argument PREDICATE is a function returning a 
non-nil
 value if the current element or object should be counted in.  It
 accepts one argument: the element or object being considered.
 This argument allows to count only a certain type of objects,
-like inline images, which are a subset of links \(in that case,
-`org-export-inline-image-p' might be an useful predicate\)."
-  (let ((counter 0)
-        ;; Determine if search should apply to current section, in
-        ;; which case it should be retrieved first, or to full parse
-        ;; tree.  As a special case, an element or object without
-        ;; a parent headline will also trigger a full search,
-        ;; notwithstanding WITHIN-SECTION value.
-        (data
-         (if (not within-section) (plist-get info :parse-tree)
-          (or (org-export-get-parent-headline element info)
-              (plist-get info :parse-tree)))))
-    ;; Increment counter until ELEMENT is found again.
-    (org-element-map
-     data (or types (org-element-type element))
-     (lambda (el)
-       (cond
-        ((equal element el) (1+ counter))
-       ((not predicate) (incf counter) nil)
-       ((funcall predicate el) (incf counter) nil)))
-     info 'first-match)))
+like inline images, which are a subset of links (in that case,
+`org-export-inline-image-p' might be an useful predicate).
+
+Return value is a list of numbers if ELEMENT is an headline or an
+item.  It is nil for keywords.  It represents the footnote number
+for footnote definitions and footnote references.  If ELEMENT is
+a target, return the same value as if ELEMENT was the closest
+table, item or headline containing the target.  In any other
+case, return the sequence number of ELEMENT among elements or
+objects of the same type."
+  ;; A target keyword, representing an invisible target, never has
+  ;; a sequence number.
+  (unless (eq (org-element-type element) 'keyword)
+    ;; Ordinal of a target object refer to the ordinal of the closest
+    ;; table, item, or headline containing the object.
+    (when (eq (org-element-type element) 'target)
+      (setq element
+           (loop for parent in (org-export-get-genealogy element info)
+                 when
+                 (memq
+                  (org-element-type parent)
+                  '(footnote-definition footnote-reference headline item
+                                         table))
+                 return parent)))
+    (case (org-element-type element)
+      ;; Special case 1: An headline returns its number as a list.
+      (headline (org-export-get-headline-number element info))
+      ;; Special case 2: An item returns its number as a list.
+      (item (let ((struct (org-element-property :structure element)))
+             (org-list-get-item-number
+              (org-element-property :begin element)
+              struct
+              (org-list-prevs-alist struct)
+              (org-list-parents-alist struct))))
+      ((footnote definition footnote-reference)
+       (org-export-get-footnote-number element info))
+      (otherwise
+       (let ((counter 0)
+            ;; Determine if search should apply to current section,
+            ;; in which case it should be retrieved first, or to full
+            ;; parse tree.  As a special case, an element or object
+            ;; without a parent headline will also trigger a full
+            ;; search, notwithstanding WITHIN-SECTION value.
+            (data
+             (if (not within-section) (plist-get info :parse-tree)
+               (or (org-export-get-parent-headline element info)
+                   (plist-get info :parse-tree)))))
+        ;; Increment counter until ELEMENT is found again.
+        (org-element-map
+         data (or types (org-element-type element))
+         (lambda (el)
+           (cond
+            ((equal element el) (1+ counter))
+            ((not predicate) (incf counter) nil)
+            ((funcall predicate el) (incf counter) nil)))
+         info 'first-match))))))
 
 
 ;;;; For Src-Blocks
diff --git a/lisp/org.el b/lisp/org.el
index a81f7fc..b8dd292 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -9896,6 +9896,22 @@ visibility around point, thus ignoring
               pos (match-beginning 0))))
       ;; There is an exact target for this
       (goto-char pos))
+     ((save-excursion
+       (goto-char (point-min))
+       (and
+        (re-search-forward
+         (format "^[ \t]*#\\+TARGET: %s" (regexp-quote s0)) nil t)
+        (setq type 'dedicated pos (match-beginning 0))))
+      ;; Found an invisible target.
+      (goto-char pos))
+     ((save-excursion
+       (goto-char (point-min))
+       (and
+        (re-search-forward
+         (format "^[ \t]*#\\+NAME: %s" (regexp-quote s0)) nil t)
+        (setq type 'dedicated pos (match-beginning 0))))
+      ;; Found an element with a matching #+name affiliated keyword.
+      (goto-char pos))
      ((and (string-match "^(\\(.*\\))$" s0)
           (save-excursion
             (goto-char (point-min))
diff --git a/testing/contrib/lisp/test-org-export.el 
b/testing/contrib/lisp/test-org-export.el
index c9923d4..f791391 100644
--- a/testing/contrib/lisp/test-org-export.el
+++ b/testing/contrib/lisp/test-org-export.el
@@ -369,3 +369,94 @@ body\n")))
        ;; Both footnotes should be seen.
        (should
         (= (length (org-export-collect-footnote-definitions tree info)) 2))))))
+
+(ert-deftest test-org-export/fuzzy-links ()
+  "Test fuzz link export specifications."
+  ;; 1. Links to invisible (keyword) targets should be ignored.
+  (org-test-with-temp-text
+      "Paragraph.\n#+TARGET: Test\n[[Test]]"
+    (let* ((tree (org-element-parse-buffer))
+          (info (org-combine-plists (org-export-initial-options))))
+      (setq info (org-combine-plists
+                 info (org-export-collect-tree-properties tree info '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-temp-text
+      "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]"
+    (let* ((tree (org-element-parse-buffer))
+          (info (org-combine-plists (org-export-initial-options))))
+      (setq info (org-combine-plists
+                 info (org-export-collect-tree-properties tree info 'test)))
+      (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-temp-text
+      "- Item1\n  - Item11\n  - <<test>>Item12\n- Item2\n\n\n[[test]]"
+    (let* ((tree (org-element-parse-buffer))
+          (info (org-combine-plists (org-export-initial-options))))
+      (setq info (org-combine-plists
+                 info (org-export-collect-tree-properties tree info '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-temp-text
+      "Paragraph[1][2][fn:lbl3:C<<target>>][[test]][[target]]\n[1] A\n\n[2] 
<<test>>B"
+    (let* ((tree (org-element-parse-buffer))
+          (info (org-combine-plists (org-export-initial-options))))
+      (setq info (org-combine-plists
+                 info (org-export-collect-tree-properties tree info 'test)))
+      (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-temp-text
+      "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]"
+    (let* ((tree (org-element-parse-buffer))
+          (info (org-combine-plists (org-export-initial-options))))
+      (setq info (org-combine-plists
+                 info (org-export-collect-tree-properties tree info 'test)))
+      (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-temp-text
+      "* Head1\n* Head2\nParagraph<<target>>\n* Head3\n[[target]]"
+    (let* ((tree (org-element-parse-buffer))
+          (info (org-combine-plists (org-export-initial-options))))
+      (setq info (org-combine-plists
+                 info (org-export-collect-tree-properties tree info 'test)))
+      (should
+       (equal '(2)
+             (org-element-map
+              tree 'link
+              (lambda (link)
+                (org-export-get-ordinal
+                 (org-export-resolve-fuzzy-link link info) info)) info t))))))
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 4fc9ac9..3639367 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -98,6 +98,47 @@ http://article.gmane.org/gmane.emacs.orgmode/21459/";
     (org-babel-next-src-block)
     (should (equal '(2 1) (org-babel-execute-src-block)))))
 
+
+
+;;; Links
+
+;;;; Fuzzy links
+
+;; Fuzzy links [[text]] encompass links to a target (<<text>>), to
+;; a target keyword (aka an invisible target: #+TARGET: text), to
+;; a named element (#+name: text) and to headlines (* Text).
+
+(ert-deftest test-org-export/fuzzy-links ()
+  "Test fuzzy links specifications."
+  ;; 1. Fuzzy link goes in priority to a matching target.
+  (org-test-with-temp-text
+      "#+TARGET: Test\n#+NAME: Test\n|a|b|\n<<Test>>\n* Test\n[[Test]]"
+    (goto-line 4)
+    (org-open-at-point)
+    (should (looking-at "<<Test>>")))
+  ;; 2. Fuzzy link should then go to a matching target keyword.
+  (org-test-with-temp-text
+      "#+NAME: Test\n|a|b|\n#+TARGET: Test\n* Test\n[[Test]]"
+    (goto-line 4)
+    (org-open-at-point)
+    (should (looking-at "#\\+TARGET: Test")))
+  ;; 3. Then fuzzy link points to an element with a given name.
+  (org-test-with-temp-text "Test\n#+NAME: Test\n|a|b|\n* Test\n[[Test]]"
+    (goto-line 5)
+    (org-open-at-point)
+    (should (looking-at "#\\+NAME: Test")))
+  ;; 4. A target still lead to a matching headline otherwise.
+  (org-test-with-temp-text "* Head1\n* Head2\n*Head3\n[[Head2]]"
+    (goto-line 4)
+    (org-open-at-point)
+    (should (looking-at "\\* Head2")))
+  ;; 5. With a leading star in link, enforce heading match.
+  (org-test-with-temp-text "#+TARGET: Test\n* Test\n<<Test>>\n[[*Test]]"
+    (goto-line 4)
+    (org-open-at-point)
+    (should (looking-at "\\* Test"))))
+
+
 (provide 'test-org)
 
 ;;; test-org.el ends here
-- 
1.7.9.2


reply via email to

[Prev in Thread] Current Thread [Next in Thread]