ox: Use a new scheme for internal references

* lisp/ox.el (org-export-get-reference): Use randomly generated labels.
(org-export-new-reference):
(org-export-format-reference): New functions.

* testing/lisp/test-ox.el (test-org-export/get-reference): New test.

The new scheme is better when datum type cannot be known ahead of time
or when references are not created sequentially, e.g., during
a publishing process where a reference to a file can be require before
the file is published.
This commit is contained in:
Nicolas Goaziou 2016-03-07 23:43:23 +01:00
parent 6ec06dcff9
commit 32c3f33d00
2 changed files with 64 additions and 16 deletions

View File

@ -4315,29 +4315,55 @@ has type \"radio\"."
;;;; For References
;;
;; `org-export-get-reference' associate a unique reference for any
;; object or element.
;; object or element. It uses `org-export-new-reference' and
;; `org-export-format-reference' to, respectively, generate new
;; internal references and turn them into a string suitable for
;; output.
;;
;; `org-export-get-ordinal' associates a sequence number to any object
;; or element.
(defun org-export-new-reference (references)
"Return a unique reference, among REFERENCES.
REFERENCES is an alist whose values are in-use references, as
numbers. Returns a number, which is the internal representation
of a reference. See also `org-export-format-reference'."
;; Generate random 7 digits hexadecimal numbers. Collisions
;; increase exponentially with the numbers of references. However,
;; the odds for encountering at least one collision with 1000 active
;; references in the same document are roughly 0.2%, so this
;; shouldn't be the bottleneck.
(let ((new (random #x10000000)))
(while (rassq new references) (setq new (random #x10000000)))
new))
(defun org-export-format-reference (reference)
"Format REFERENCE into a string.
REFERENCE is a number representing a reference, as returned by
`org-export-new-reference', which see."
(format "org%x" reference))
(defun org-export-get-reference (datum info)
"Return a unique reference for DATUM, as a string.
DATUM is either an element or an object. INFO is the current
export state, as a plist. Returned reference consists of
alphanumeric characters only."
(let ((type (org-element-type datum))
(cache (or (plist-get info :internal-references)
(let ((h (make-hash-table :test #'eq)))
(plist-put info :internal-references h)
h))))
(or (gethash datum cache)
(puthash datum
(format "org%s%d"
(if type
(replace-regexp-in-string "-" "" (symbol-name type))
"secondarystring")
(cl-incf (gethash type cache 0)))
cache))))
export state, as a plist.
This functions checks `:crossrefs' property in INFO for search
cells matching DATUM before creating a new reference. Returned
reference consists of alphanumeric characters only."
(let ((cache (plist-get info :internal-references)))
(or (car (rassq datum cache))
(let* ((new (org-export-new-reference cache))
(search-cells (org-export-search-cells datum))
(reference-string (org-export-format-reference new)))
;; Cache contains both data already associated to
;; a reference and in-use internal references, so as to make
;; unique references.
(push (cons search-cells new) cache)
(push (cons reference-string datum) cache)
(plist-put info :internal-references cache)
reference-string))))
(defun org-export-get-ordinal (element info &optional types predicate)
"Return ordinal number of an element or object.

View File

@ -2873,6 +2873,28 @@ Another text. (ref:text)
(should (equal (org-export-file-uri "~/file.org")
(concat "file://" (expand-file-name "~/file.org")))))
(ert-deftest test-org-export/get-reference ()
"Test `org-export-get-reference' specifications."
(should
(org-test-with-parsed-data "* Headline"
(org-export-get-reference (org-element-map tree 'headline #'identity nil t)
info)))
;; For a given element always return the same reference.
(should
(org-test-with-parsed-data "* Headline"
(let ((headline (org-element-map tree 'headline #'identity nil t)))
(equal (org-export-get-reference headline info)
(org-export-get-reference headline info)))))
;; Use search cells defined in `:crossrefs'.
(should
(equal "org1"
(org-test-with-parsed-data "* Headline"
(let* ((headline (org-element-map tree 'headline #'identity nil t))
(search-cell (car (org-export-search-cells headline))))
(setq info
(plist-put info :crossrefs (list (cons search-cell 1))))
(org-export-get-reference headline info))))))
;;; Src-block and example-block