ox-html: Prevent spurious target below headlines

* lisp/ox-html.el (org-html-headline):
(org-html-link): Do not insert an additional target.

* lisp/ox-publish.el (org-publish-resolve-external-link): Add an
  optional argument.

* lisp/ox.el (org-export-get-reference): Improve docstring.

* testing/examples/pub/a.org:
* testing/examples/pub/b.org: New files.
* testing/lisp/test-ox-publish.el (test-org-publish/resolve-external-link):
  New test.
This commit is contained in:
Nicolas Goaziou 2017-09-10 00:16:12 +02:00
parent b3ab012d69
commit 007bbddbcc
7 changed files with 124 additions and 46 deletions

View File

@ -58,6 +58,10 @@ size.
,#+STARTUP: shrink
#+END_EXAMPLE
** Miscellaneous
*** ~org-publish-resolve-external-link~ accepts a new optional argument.
* Version 9.1
** Incompatible changes

View File

@ -2599,18 +2599,8 @@ holding contextual information."
(full-text (funcall (plist-get info :html-format-headline-function)
todo todo-type priority text tags info))
(contents (or contents ""))
(ids (delq nil
(list (org-element-property :CUSTOM_ID headline)
(org-export-get-reference headline info)
(org-element-property :ID headline))))
(preferred-id (car ids))
(extra-ids
(mapconcat
(lambda (id)
(org-html--anchor
(if (org-uuidgen-p id) (concat "ID-" id) id)
nil nil info))
(cdr ids) "")))
(id (or (org-element-property :CUSTOM_ID headline)
(org-export-get-reference headline info))))
(if (org-export-low-level-p headline info)
;; This is a deep sub-tree: export it as a list item.
(let* ((html-type (if numberedp "ol" "ul")))
@ -2619,11 +2609,9 @@ holding contextual information."
(apply #'format "<%s class=\"org-%s\">\n"
(make-list 2 html-type)))
(org-html-format-list-item
contents (if numberedp 'ordered 'unordered)
nil info nil
(concat (org-html--anchor preferred-id nil nil info)
extra-ids
full-text)) "\n"
contents (if numberedp 'ordered 'unordered)
nil info nil
(concat (org-html--anchor id nil nil info) full-text)) "\n"
(and (org-export-last-sibling-p headline info)
(format "</%s>\n" html-type))))
;; Standard headline. Export it as a section.
@ -2636,10 +2624,9 @@ holding contextual information."
(concat (format "outline-%d" level)
(and extra-class " ")
extra-class)
(format "\n<h%d id=\"%s\">%s%s</h%d>\n"
(format "\n<h%d id=\"%s\">%s</h%d>\n"
level
preferred-id
extra-ids
id
(concat
(and numberedp
(format
@ -3010,16 +2997,11 @@ INFO is a plist holding contextual information. See
;; relative to a custom-id, a headline title, a name or
;; a target.
(let ((option (org-element-property :search-option link)))
(cond ((not option) raw-path)
;; Since HTML back-end use custom-id value as-is,
;; resolving is them is trivial.
((eq (string-to-char option) ?#) (concat raw-path option))
(t
(concat raw-path
"#"
(org-publish-resolve-external-link
option
(org-element-property :path link)))))))
(if (not option) raw-path
(let ((path (org-element-property :path link)))
(concat raw-path
"#"
(org-publish-resolve-external-link option path t))))))
(t raw-path)))
;; Extract attributes from parent's paragraph. HACK: Only do
;; this for the first link in parent (inner image link for

View File

@ -1131,7 +1131,7 @@ This function is meant to be used as a final output filter. See
;; Return output unchanged.
output)
(defun org-publish-resolve-external-link (search file)
(defun org-publish-resolve-external-link (search file &optional prefer-custom)
"Return reference for element matching string SEARCH in FILE.
Return value is an internal reference, as a string.
@ -1139,18 +1139,31 @@ Return value is an internal reference, as a string.
This function allows resolving external links with a search
option, e.g.,
[[file.org::*heading][description]]
[[file.org::#custom-id][description]]
[[file.org::fuzzy][description]]
[[file:file.org::*heading][description]]
[[file:file.org::#custom-id][description]]
[[file:file.org::fuzzy][description]]
When PREFER-CUSTOM is non-nil, and SEARCH targets a headline in
FILE, return its custom ID, if any.
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"
search
file)
"MissingReference")
(cond
((and prefer-custom
(if (string-prefix-p "#" search)
(substring search 1)
(with-current-buffer (find-file-noselect file)
(org-with-point-at 1
(org-link-search search nil t)
(and (org-at-heading-p)
(org-string-nw-p (org-entry-get (point) "CUSTOM_ID"))))))))
((not org-publish-cache)
(progn
(message "Reference %S in file %S cannot be resolved without publishing"
search
file)
"MissingReference"))
(t
(let* ((filename (file-truename file))
(crossrefs
(org-publish-cache-get-file-property filename :crossrefs nil t))
@ -1167,7 +1180,7 @@ references with `org-export-get-reference'."
(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))))))
(org-export-format-reference new)))))))

View File

@ -4436,9 +4436,19 @@ REFERENCE is a number representing a reference, as returned by
DATUM is either an element or an object. INFO is the current
export state, as a plist.
This function checks `:crossrefs' property in INFO for search
cells matching DATUM before creating a new reference. Returned
reference consists of alphanumeric characters only."
References for the current document are stored in
`:internal-references' property. Its value is an alist with
associations of the following types:
(REFERENCE . DATUM) and (SEARCH-CELL . ID)
REFERENCE is the reference string to be used for object or
element DATUM. SEARCH-CELL is a search cell, as returned by
`org-export-search-cells'. ID is a number or a string uniquely
identifying DATUM within the document.
This function also checks `:crossrefs' property for search cells
matching DATUM before creating a new reference."
(let ((cache (plist-get info :internal-references)))
(or (car (rassq datum cache))
(let* ((crossrefs (plist-get info :crossrefs))

View File

@ -1,4 +1,9 @@
#+title: A
#+date: <2014-03-04 Tue>
Contents
* Headline1
:PROPERTIES:
:CUSTOM_ID: a1
:END:
[[file:b.org::*Headline1]]

View File

@ -1,3 +1,6 @@
#+title: b
#+date: <2012-03-29 Thu>
* Headline1
[[file:a.org::#a1]]

View File

@ -94,7 +94,6 @@ Unless set otherwise in PROPERTIES, `:base-directory' is set to
(cl-remove-if #'file-directory-p
(directory-files dir))))))))
;;; Site-map
@ -327,6 +326,68 @@ Unless set otherwise in PROPERTIES, `:base-directory' is set to
(insert-file-contents (expand-file-name "sitemap.org" dir))
(buffer-string)))))))
;;; Cross references
(ert-deftest test-org-publish/resolve-external-link ()
"Test `org-publish-resolve-external-link' specifications."
;; Function should preserve internal reference when used between
;; published files.
(should
(apply
#'equal
(let* ((ids nil)
(backend
(org-export-create-backend
:transcoders
'((headline . (lambda (h c i)
(concat (org-export-get-reference h i) " " c)))
(paragraph . (lambda (p c i) c))
(section . (lambda (s c i) c))
(link . (lambda (l c i)
(let ((option (org-element-property :search-option l))
(path (org-element-property :path l)))
(and option
(org-publish-resolve-external-link
option path))))))))
(publish
(lambda (plist filename pub-dir)
(org-publish-org-to backend filename ".test" plist pub-dir))))
(org-test-publish
(list :publishing-function (list publish))
(lambda (dir)
(cl-subseq
(split-string
(mapconcat (lambda (f) (org-file-contents (expand-file-name f dir)))
(directory-files dir nil "\\.test\\'")
" "))
1 3))))))
;; When optional argument PREFER-CUSTOM is non-nil, use custom ID
;; instead of internal reference, whenever possible.
(should
(equal
"a1"
(let* ((ids nil)
(backend
(org-export-create-backend
:transcoders
'((headline . (lambda (h c i) c))
(paragraph . (lambda (p c i) c))
(section . (lambda (s c i) c))
(link . (lambda (l c i)
(let ((option (org-element-property :search-option l))
(path (org-element-property :path l)))
(when option
(throw :exit (org-publish-resolve-external-link
option path t)))))))))
(publish
(lambda (plist filename pub-dir)
(push (catch :exit
(org-publish-org-to backend filename ".test" plist pub-dir))
ids))))
(org-test-publish (list :publishing-function (list publish)) #'ignore)
(car ids)))))
;;; Tools