Kaynağa Gözat

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.
Nicolas Goaziou 7 yıl önce
ebeveyn
işleme
007bbddbcc

+ 4 - 0
etc/ORG-NEWS

@@ -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

+ 12 - 30
lisp/ox-html.el

@@ -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

+ 24 - 11
lisp/ox-publish.el

@@ -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)))))))
 
 
 

+ 13 - 3
lisp/ox.el

@@ -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))

+ 6 - 1
testing/examples/pub/a.org

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

+ 3 - 0
testing/examples/pub/b.org

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

+ 62 - 1
testing/lisp/test-ox-publish.el

@@ -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