mirror of
https://git.savannah.gnu.org/git/emacs/org-mode.git
synced 2024-12-23 10:34:17 +00:00
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))
|
||||
;; Check if a buffer visiting FILENAME is already open.
|
||||
(let* ((org-inhibit-startup t)
|
||||
(visitingp (find-buffer-visiting filename))
|
||||
(work-buffer (or visitingp (find-file-noselect filename))))
|
||||
(prog1 (with-current-buffer work-buffer
|
||||
(let ((output-file
|
||||
(org-export-output-file-name extension nil pub-dir))
|
||||
(body-p (plist-get plist :body-only)))
|
||||
(org-export-to-file backend output-file
|
||||
nil nil nil body-p
|
||||
;; Add `org-publish--collect-references' and
|
||||
;; `org-publish-collect-index' to final output
|
||||
;; filters. The latter isn't dependent on
|
||||
;; `:makeindex', since we want to keep it up-to-date
|
||||
;; in cache anyway.
|
||||
(org-combine-plists
|
||||
plist
|
||||
`(:filter-final-output
|
||||
,(cons 'org-publish--collect-references
|
||||
(cons 'org-publish-collect-index
|
||||
(plist-get plist :filter-final-output))))))))
|
||||
(visiting (find-buffer-visiting filename))
|
||||
(work-buffer (or visiting (find-file-noselect filename))))
|
||||
(unwind-protect
|
||||
(with-current-buffer work-buffer
|
||||
(let ((output (org-export-output-file-name extension nil pub-dir)))
|
||||
(org-export-to-file backend output
|
||||
nil nil nil (plist-get plist :body-only)
|
||||
;; Add `org-publish--store-crossrefs' and
|
||||
;; `org-publish-collect-index' to final output filters.
|
||||
;; The latter isn't dependent on `:makeindex', since we
|
||||
;; want to keep it up-to-date in cache anyway.
|
||||
(org-combine-plists
|
||||
plist
|
||||
`(:filter-final-output
|
||||
(org-publish--store-crossrefs
|
||||
org-publish-collect-index
|
||||
,@(plist-get plist :filter-final-output)))))))
|
||||
;; 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)
|
||||
"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]]
|
||||
;; links, where "file.org" belongs to the current project.
|
||||
|
||||
(defun org-publish--collect-references (output _backend info)
|
||||
"Store references for current published file.
|
||||
(defun org-publish--store-crossrefs (output _backend info)
|
||||
"Store cross-references for current published file.
|
||||
|
||||
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
|
||||
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
|
||||
`org-publish-org-to'."
|
||||
(org-publish-cache-set-file-property
|
||||
(plist-get info :input-file) :references
|
||||
(let (refs)
|
||||
(when (hash-table-p (plist-get info :internal-references))
|
||||
(maphash
|
||||
(lambda (k v)
|
||||
(pcase (org-element-type k)
|
||||
(`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)))
|
||||
refs))
|
||||
(plist-get info :input-file) :crossrefs
|
||||
;; Update `:crossrefs' so as to remove unused references and search
|
||||
;; cells. Actually used references are extracted from
|
||||
;; `:internal-references', with references as strings removed. See
|
||||
;; `org-export-get-reference' for details.
|
||||
(cl-remove-if (lambda (pair) (stringp (car pair)))
|
||||
(plist-get info :internal-references)))
|
||||
;; Return output unchanged.
|
||||
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.
|
||||
|
||||
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::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)
|
||||
(progn
|
||||
(message "Reference \"%s\" in file \"%s\" cannot be resolved without \
|
||||
publishing"
|
||||
(message "Reference %S in file %S cannot be resolved without publishing"
|
||||
search
|
||||
file)
|
||||
"MissingReference")
|
||||
(let ((references (org-publish-cache-get-file-property
|
||||
(expand-file-name file) :references nil t)))
|
||||
(cond
|
||||
((cdr (pcase (aref search 0)
|
||||
(?* (assoc (cons 'headline (org-split-string (substring search 1)))
|
||||
references))
|
||||
(?# (assoc (cons 'custom-id (substring search 1)) references))
|
||||
(_
|
||||
(let ((s (org-split-string search)))
|
||||
(or (assoc (cons 'target s) references)
|
||||
(assoc (cons 'other s) references)
|
||||
(assoc (cons 'headline s) references)))))))
|
||||
(t (message "Unknown cross-reference \"%s\" in file \"%s\"" search file)
|
||||
"MissingReference")))))
|
||||
(let* ((filename (expand-file-name file))
|
||||
(crossrefs
|
||||
(org-publish-cache-get-file-property filename :crossrefs nil t))
|
||||
(cells (org-export-string-to-search-cell search)))
|
||||
(or
|
||||
;; Look for reference associated to search cells triggered by
|
||||
;; LINK. It can match when targeted file has been published
|
||||
;; already.
|
||||
(let ((known (cdr (cl-some (lambda (c) (assoc c crossrefs)) cells))))
|
||||
(and known (org-export-format-reference known)))
|
||||
;; Search cell is unknown so far. Generate a new internal
|
||||
;; reference that will be used when the targeted file will be
|
||||
;; published.
|
||||
(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."
|
||||
(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))
|
||||
(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))
|
||||
(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.
|
||||
(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)
|
||||
(plist-put info :internal-references cache)
|
||||
reference-string))))
|
||||
|
Loading…
Reference in New Issue
Block a user