forked from mirrors/org-mode
ox-publish: Better handling of cross-references
* lisp/ox-publish.el (org-publish--collect-references): Renamed... (org-publish--store-crossrefs): ... to this. (org-publish-org-to): Use previous function. Small refactoring. (org-publish-resolve-external-link): Use tight integration with `org-export-get-reference' so as to provide reliable cross references. * lisp/ox.el (org-export-get-reference): Conversely, take into consideration references suggested by `org-publish-resolve-external-link'.
This commit is contained in:
parent
32c3f33d00
commit
bef3fc6f82
|
@ -568,27 +568,25 @@ Return output file name."
|
||||||
(unless (or (not pub-dir) (file-exists-p pub-dir)) (make-directory pub-dir t))
|
(unless (or (not pub-dir) (file-exists-p pub-dir)) (make-directory pub-dir t))
|
||||||
;; Check if a buffer visiting FILENAME is already open.
|
;; Check if a buffer visiting FILENAME is already open.
|
||||||
(let* ((org-inhibit-startup t)
|
(let* ((org-inhibit-startup t)
|
||||||
(visitingp (find-buffer-visiting filename))
|
(visiting (find-buffer-visiting filename))
|
||||||
(work-buffer (or visitingp (find-file-noselect filename))))
|
(work-buffer (or visiting (find-file-noselect filename))))
|
||||||
(prog1 (with-current-buffer work-buffer
|
(unwind-protect
|
||||||
(let ((output-file
|
(with-current-buffer work-buffer
|
||||||
(org-export-output-file-name extension nil pub-dir))
|
(let ((output (org-export-output-file-name extension nil pub-dir)))
|
||||||
(body-p (plist-get plist :body-only)))
|
(org-export-to-file backend output
|
||||||
(org-export-to-file backend output-file
|
nil nil nil (plist-get plist :body-only)
|
||||||
nil nil nil body-p
|
;; Add `org-publish--store-crossrefs' and
|
||||||
;; Add `org-publish--collect-references' and
|
;; `org-publish-collect-index' to final output filters.
|
||||||
;; `org-publish-collect-index' to final output
|
;; The latter isn't dependent on `:makeindex', since we
|
||||||
;; filters. The latter isn't dependent on
|
;; want to keep it up-to-date in cache anyway.
|
||||||
;; `:makeindex', since we want to keep it up-to-date
|
|
||||||
;; in cache anyway.
|
|
||||||
(org-combine-plists
|
(org-combine-plists
|
||||||
plist
|
plist
|
||||||
`(:filter-final-output
|
`(:filter-final-output
|
||||||
,(cons 'org-publish--collect-references
|
(org-publish--store-crossrefs
|
||||||
(cons 'org-publish-collect-index
|
org-publish-collect-index
|
||||||
(plist-get plist :filter-final-output))))))))
|
,@(plist-get plist :filter-final-output)))))))
|
||||||
;; Remove opened buffer in the process.
|
;; Remove opened buffer in the process.
|
||||||
(unless visitingp (kill-buffer work-buffer)))))
|
(unless visiting (kill-buffer work-buffer)))))
|
||||||
|
|
||||||
(defun org-publish-attachment (_plist filename pub-dir)
|
(defun org-publish-attachment (_plist filename pub-dir)
|
||||||
"Publish a file with no transformation of any kind.
|
"Publish a file with no transformation of any kind.
|
||||||
|
@ -1061,68 +1059,23 @@ publishing directory."
|
||||||
;; This part implements tools to resolve [[file.org::*Some headline]]
|
;; This part implements tools to resolve [[file.org::*Some headline]]
|
||||||
;; links, where "file.org" belongs to the current project.
|
;; links, where "file.org" belongs to the current project.
|
||||||
|
|
||||||
(defun org-publish--collect-references (output _backend info)
|
(defun org-publish--store-crossrefs (output _backend info)
|
||||||
"Store references for current published file.
|
"Store cross-references for current published file.
|
||||||
|
|
||||||
OUPUT is the produced output, as a string. BACKEND is the export
|
OUPUT is the produced output, as a string. BACKEND is the export
|
||||||
back-end used, as a symbol. INFO is the final export state, as
|
back-end used, as a symbol. INFO is the final export state, as
|
||||||
a plist.
|
a plist.
|
||||||
|
|
||||||
References are stored as an alist ((TYPE SEARCH) . VALUE) where
|
|
||||||
|
|
||||||
TYPE is a symbol among `headline', `custom-id', `target' and
|
|
||||||
`other'.
|
|
||||||
|
|
||||||
SEARCH is the string a link is expected to match. It is
|
|
||||||
|
|
||||||
- headline's title, as a string, with all whitespace
|
|
||||||
characters and statistics cookies removed, if TYPE is
|
|
||||||
`headline'.
|
|
||||||
|
|
||||||
- CUSTOM_ID value if TYPE is `custom-id'.
|
|
||||||
|
|
||||||
- target's or radio-target's name if TYPE is `target'.
|
|
||||||
|
|
||||||
- NAME affiliated keyword is TYPE is `other'.
|
|
||||||
|
|
||||||
VALUE is an internal reference used in the document, as
|
|
||||||
a string.
|
|
||||||
|
|
||||||
This function is meant to be used as a final output filter. See
|
This function is meant to be used as a final output filter. See
|
||||||
`org-publish-org-to'."
|
`org-publish-org-to'."
|
||||||
(org-publish-cache-set-file-property
|
(org-publish-cache-set-file-property
|
||||||
(plist-get info :input-file) :references
|
(plist-get info :input-file) :crossrefs
|
||||||
(let (refs)
|
;; Update `:crossrefs' so as to remove unused references and search
|
||||||
(when (hash-table-p (plist-get info :internal-references))
|
;; cells. Actually used references are extracted from
|
||||||
(maphash
|
;; `:internal-references', with references as strings removed. See
|
||||||
(lambda (k v)
|
;; `org-export-get-reference' for details.
|
||||||
(pcase (org-element-type k)
|
(cl-remove-if (lambda (pair) (stringp (car pair)))
|
||||||
(`nil nil)
|
|
||||||
((or `headline `inlinetask)
|
|
||||||
(push (cons
|
|
||||||
(cons 'headline
|
|
||||||
(org-split-string
|
|
||||||
(replace-regexp-in-string
|
|
||||||
"\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
|
|
||||||
(org-element-property :raw-value k))))
|
|
||||||
v)
|
|
||||||
refs)
|
|
||||||
(let ((custom-id (org-element-property :CUSTOM_ID k)))
|
|
||||||
(when custom-id
|
|
||||||
(push (cons (cons 'custom-id custom-id) v) refs))))
|
|
||||||
((or `radio-target `target)
|
|
||||||
(push
|
|
||||||
(cons (cons 'target
|
|
||||||
(org-split-string (org-element-property :value k)))
|
|
||||||
v)
|
|
||||||
refs))
|
|
||||||
((and (let name (org-element-property :name k))
|
|
||||||
(guard name))
|
|
||||||
(push (cons (cons 'other (org-split-string name)) v)
|
|
||||||
refs)))
|
|
||||||
refs)
|
|
||||||
(plist-get info :internal-references)))
|
(plist-get info :internal-references)))
|
||||||
refs))
|
|
||||||
;; Return output unchanged.
|
;; Return output unchanged.
|
||||||
output)
|
output)
|
||||||
|
|
||||||
|
@ -1131,32 +1084,38 @@ This function is meant to be used as a final output filter. See
|
||||||
|
|
||||||
Return value is an internal reference, as a string.
|
Return value is an internal reference, as a string.
|
||||||
|
|
||||||
This function allows the resolution of external links like:
|
This function allows resolving external links with a search
|
||||||
|
option, e.g.,
|
||||||
|
|
||||||
[[file.org::*fuzzy][description]]
|
[[file.org::*heading][description]]
|
||||||
[[file.org::#custom-id][description]]
|
[[file.org::#custom-id][description]]
|
||||||
[[file.org::fuzzy][description]]"
|
[[file.org::fuzzy][description]]
|
||||||
|
|
||||||
|
It only makes sense to use this if export back-end builds
|
||||||
|
references with `org-export-get-reference'."
|
||||||
(if (not org-publish-cache)
|
(if (not org-publish-cache)
|
||||||
(progn
|
(progn
|
||||||
(message "Reference \"%s\" in file \"%s\" cannot be resolved without \
|
(message "Reference %S in file %S cannot be resolved without publishing"
|
||||||
publishing"
|
|
||||||
search
|
search
|
||||||
file)
|
file)
|
||||||
"MissingReference")
|
"MissingReference")
|
||||||
(let ((references (org-publish-cache-get-file-property
|
(let* ((filename (expand-file-name file))
|
||||||
(expand-file-name file) :references nil t)))
|
(crossrefs
|
||||||
(cond
|
(org-publish-cache-get-file-property filename :crossrefs nil t))
|
||||||
((cdr (pcase (aref search 0)
|
(cells (org-export-string-to-search-cell search)))
|
||||||
(?* (assoc (cons 'headline (org-split-string (substring search 1)))
|
(or
|
||||||
references))
|
;; Look for reference associated to search cells triggered by
|
||||||
(?# (assoc (cons 'custom-id (substring search 1)) references))
|
;; LINK. It can match when targeted file has been published
|
||||||
(_
|
;; already.
|
||||||
(let ((s (org-split-string search)))
|
(let ((known (cdr (cl-some (lambda (c) (assoc c crossrefs)) cells))))
|
||||||
(or (assoc (cons 'target s) references)
|
(and known (org-export-format-reference known)))
|
||||||
(assoc (cons 'other s) references)
|
;; Search cell is unknown so far. Generate a new internal
|
||||||
(assoc (cons 'headline s) references)))))))
|
;; reference that will be used when the targeted file will be
|
||||||
(t (message "Unknown cross-reference \"%s\" in file \"%s\"" search file)
|
;; published.
|
||||||
"MissingReference")))))
|
(let ((new (org-export-new-reference crossrefs)))
|
||||||
|
(dolist (cell cells) (push (cons cell new) crossrefs))
|
||||||
|
(org-publish-cache-set-file-property filename :crossrefs crossrefs)
|
||||||
|
(org-export-format-reference new))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
14
lisp/ox.el
14
lisp/ox.el
|
@ -4354,13 +4354,21 @@ cells matching DATUM before creating a new reference. Returned
|
||||||
reference consists of alphanumeric characters only."
|
reference consists of alphanumeric characters only."
|
||||||
(let ((cache (plist-get info :internal-references)))
|
(let ((cache (plist-get info :internal-references)))
|
||||||
(or (car (rassq datum cache))
|
(or (car (rassq datum cache))
|
||||||
(let* ((new (org-export-new-reference cache))
|
(let* ((crossrefs (plist-get info :crossrefs))
|
||||||
(search-cells (org-export-search-cells datum))
|
(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))
|
||||||
|
(org-export-new-reference cache)))
|
||||||
(reference-string (org-export-format-reference new)))
|
(reference-string (org-export-format-reference new)))
|
||||||
;; Cache contains both data already associated to
|
;; Cache contains both data already associated to
|
||||||
;; a reference and in-use internal references, so as to make
|
;; a reference and in-use internal references, so as to make
|
||||||
;; unique references.
|
;; unique references.
|
||||||
(push (cons search-cells new) cache)
|
(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.
|
||||||
(push (cons reference-string datum) cache)
|
(push (cons reference-string datum) cache)
|
||||||
(plist-put info :internal-references cache)
|
(plist-put info :internal-references cache)
|
||||||
reference-string))))
|
reference-string))))
|
||||||
|
|
Loading…
Reference in a new issue