From 32c3f33d00b3dc9affa44b4af432dc349d41e444 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Mon, 7 Mar 2016 23:43:23 +0100 Subject: [PATCH] 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. --- lisp/ox.el | 58 +++++++++++++++++++++++++++++------------ testing/lisp/test-ox.el | 22 ++++++++++++++++ 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/lisp/ox.el b/lisp/ox.el index bb94559ac..4bb58f62d 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -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. diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index 0b89d4265..f51288f5d 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -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