ox: Fix duplicate internal references for same title headings

* lisp/ox.el (org-export-get-reference): Ensure different elements or
  objects generating the same search cell do not get the same internal
  reference.
* testing/lisp/test-ox.el (test-org-export/get-reference): Add test.

Reported-by: mstrey@strey.biz
<http://permalink.gmane.org/gmane.emacs.orgmode/110211>
This commit is contained in:
Nicolas Goaziou 2016-11-11 17:47:09 +01:00
parent a3c366eb98
commit 27b10fb265
2 changed files with 35 additions and 9 deletions

View File

@ -4385,19 +4385,35 @@ reference consists of alphanumeric characters only."
(or (car (rassq datum cache))
(let* ((crossrefs (plist-get info :crossrefs))
(cells (org-export-search-cells datum))
;; If any other published document relies on an
;; association between a search cell and a reference,
;; make sure to preserve it. See
;; `org-publish-resolve-external-link' for details.
(new (or (cdr (cl-some (lambda (c) (assoc c crossrefs)) cells))
;; Preserve any pre-existing association between
;; a search cell and a reference, i.e., when some
;; previously published document referenced a location
;; within current file (see
;; `org-publish-resolve-external-link').
;;
;; However, there is no guarantee that search cells are
;; unique, e.g., there might be duplicate custom ID or
;; two headings with the same title in the file.
;;
;; As a consequence, before re-using any reference to
;; an element or object, we check that it doesn't refer
;; to a previous element or object.
(new (or (cl-some
(lambda (cell)
(let ((stored (cdr (assoc cell crossrefs))))
(when stored
(let ((old (org-export-format-reference stored)))
(and (not (assoc old cache)) stored)))))
cells)
(org-export-new-reference cache)))
(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.
(dolist (cell cells) (push (cons cell new) cache))
;; Keep an associated related to DATUM as not every object
;; and element can be associated to a search cell.
;; Retain a direct association between reference string and
;; DATUM since (1) not every object or element can be given
;; a search cell (2) it permits quick lookup.
(push (cons reference-string datum) cache)
(plist-put info :internal-references cache)
reference-string))))

View File

@ -3185,7 +3185,8 @@ Another text. (ref:text)
(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'.
;; Use search cells defined in `:crossrefs'. However, handle
;; duplicate search cells.
(should
(equal "org0000001"
(org-test-with-parsed-data "* Headline"
@ -3193,7 +3194,16 @@ Another text. (ref:text)
(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))))))
(org-export-get-reference headline info)))))
(should-not
(equal '("org0000001" "org0000001")
(org-test-with-parsed-data "* H\n** H"
(org-element-map tree 'headline
(lambda (h)
(let* ((search-cell (car (org-export-search-cells h)))
(info (plist-put info :crossrefs
(list (cons search-cell 1)))))
(org-export-get-reference h info))))))))
;;; Pseudo objects and pseudo elements