From bef3fc6f821dd7533308a0849963fef2b3f889df Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Thu, 10 Mar 2016 21:50:44 +0100 Subject: [PATCH] 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'. --- lisp/ox-publish.el | 145 ++++++++++++++++----------------------------- lisp/ox.el | 14 ++++- 2 files changed, 63 insertions(+), 96 deletions(-) diff --git a/lisp/ox-publish.el b/lisp/ox-publish.el index 27f06ffa6..92d74081a 100644 --- a/lisp/ox-publish.el +++ b/lisp/ox-publish.el @@ -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)))))) diff --git a/lisp/ox.el b/lisp/ox.el index 4bb58f62d..d93493f76 100644 --- a/lisp/ox.el +++ b/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))))