Hi,

I'm happy to finally be able to send an updated version of this patch
that touches most backends in lisp/, but not the manual.  I have been
moving over the summer etc.

You now specify unnumbered headlines with properties.  I think this is
better since being unnumbered it's a pretty permanent state.  It's
pretty hard to discover though, other than by looking at the output.

So this works as expected:

* Some headline
  :PROPERTIES:
  :UNNUMBERED: t
  :END:

There's no :NUMBERED property and :UNNUMBERED is hardcoded.

I introduce a new function `org-export-get-headline-id` which returns
the first non-nil from the following list.  There's a caveat:
CUSTOM_ID is ensured to be unique!  Did I open the famous can of worm?

1. The CUSTOM_ID property.
2. A relative level number if the headline is numbered.
3. The ID property
4. A new generated unique ID.

Anyhow, `org-export-get-headline-id' ensures that we can refer to
unnumbered headlines, which was not possible before.  Of course, in
LaTeX such ref to a \section* will be nonsense, so we could introduce
a \pageref here.  I'm unsure about whether this conflicts
`org-latex-custom-id-as-label' which I had never seen until today
(also notes on this in patch).

I have updated backends in lisp/, but I'm at most(!) an "expert" on
LaTeX.  However, I have tested all backends to the best of my ability.

Please feel free to test and let me know about any discrepancies!

Cheers,
Rasmus

PS: Not knowing or caring much about md, the links generated by it to
headlines seem wrong.  Referring to headline 1 it only prints "1".
Should it be something like "[LABEL](1)"?


-- 
This is the kind of tedious nonsense up with which I will not put
>From fb44a552a151025513b645527498325febb6118f Mon Sep 17 00:00:00 2001
From: Rasmus <ras...@gmx.us>
Date: Fri, 8 Aug 2014 14:53:01 +0200
Subject: [PATCH] ox: Support unnumbered headlines via property.

* ox.el (org-export--collect-headline-numbering): Return nil
if unnumbered headline.
(org-export-get-headline-id): New defun that returns a unique
ID to a headline.
(org-export-numbered-headline-p): Also tests for unnumbered
headline.
* ox-odt.el (org-odt-headline, org-odt-link,
org-odt-link--infer-description): Support unnumbered
headline.
* ox-md.el (org-md-headline, org-md-link): Support
unnumbered headlines.
* ox-latex.el (org-latex-headline, org.latex-link): Support
unnumbered headlines.
* ox-html.el (org-html-headline, org-html-link): Support
unnumbered headlines.
* ox-ascii.el (org-ascii-link): Support ununbered headlines.

Headlines can now be specified as unnumbered by assigning the property :UNUMBERED.
---
 lisp/ox-ascii.el |  7 +++++--
 lisp/ox-html.el  | 22 ++++------------------
 lisp/ox-latex.el | 22 ++--------------------
 lisp/ox-md.el    | 25 +++++++++++++------------
 lisp/ox-odt.el   | 32 ++++++++++++++++++--------------
 lisp/ox.el       | 44 ++++++++++++++++++++++++++++++++++++++++----
 6 files changed, 82 insertions(+), 70 deletions(-)

diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el
index 047b74e..8a5ad89 100644
--- a/lisp/ox-ascii.el
+++ b/lisp/ox-ascii.el
@@ -1511,9 +1511,12 @@ INFO is a plist holding contextual information."
 	    (let ((number
 		   (org-export-get-ordinal
 		    destination info nil 'org-ascii--has-caption-p)))
-	      (when number
+	      (if number
 		(if (atom number) (number-to-string number)
-		  (mapconcat 'number-to-string number "."))))))))
+		  (mapconcat 'number-to-string number "."))
+		;; unnumbered headline
+		(when (equal 'headline (org-element-type destination))
+		  (format "[%s]" (org-export-data (org-export-get-alt-title destination info) info)))))))))
      (t
       (if (not (org-string-nw-p desc)) (format "[%s]" raw-link)
 	(concat (format "[%s]" desc)
diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index 1d424cc..94cee20 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -2321,7 +2321,7 @@ holding contextual information."
   (unless (org-element-property :footnote-section-p headline)
     (let* ((numberedp (org-export-numbered-headline-p headline info))
            (numbers (org-export-get-headline-number headline info))
-           (section-number (mapconcat #'number-to-string numbers "-"))
+           (section-number (when numbers (mapconcat #'number-to-string numbers "-")))
            (level (+ (org-export-get-relative-level headline info)
                      (1- (plist-get info :html-toplevel-hlevel))))
            (todo (and (plist-get info :with-todo-keywords)
@@ -2338,9 +2338,9 @@ holding contextual information."
            (contents (or contents ""))
            (ids (delq nil
                       (list (org-element-property :CUSTOM_ID headline)
-                            (concat "sec-" section-number)
+                            (when section-number (concat "sec-" section-number))
                             (org-element-property :ID headline))))
-           (preferred-id (car ids))
+           (preferred-id (org-export-get-headline-id headline info))
            (extra-ids (mapconcat
                        (lambda (id)
                          (org-html--anchor
@@ -2807,21 +2807,7 @@ INFO is a plist holding contextual information.  See
 			(org-element-property :raw-link link) info))))
 	  ;; Link points to a headline.
 	  (headline
-	   (let ((href
-		  ;; What href to use?
-		  (cond
-		   ;; Case 1: Headline is linked via it's CUSTOM_ID
-		   ;; property.  Use CUSTOM_ID.
-		   ((string= type "custom-id")
-		    (org-element-property :CUSTOM_ID destination))
-		   ;; Case 2: Headline is linked via it's ID property
-		   ;; or through other means.  Use the default href.
-		   ((member type '("id" "fuzzy"))
-		    (format "sec-%s"
-			    (mapconcat 'number-to-string
-				       (org-export-get-headline-number
-					destination info) "-")))
-		   (t (error "Shouldn't reach here"))))
+	   (let ((href (org-export-get-headline-id destination info))
 		 ;; What description to use?
 		 (desc
 		  ;; Case 1: Headline is numbered and LINK has no
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index f59d6b2..860b9f7 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -1476,15 +1476,7 @@ holding contextual information."
 			       todo todo-type priority text tags info))
 	   ;; Associate \label to the headline for internal links.
 	   (headline-label
-	    (let ((custom-label
-		   (and (plist-get info :latex-custom-id-labels)
-			(org-element-property :CUSTOM_ID headline))))
-	      (if custom-label (format "\\label{%s}\n" custom-label)
-		(format "\\label{sec-%s}\n"
-			(mapconcat
-			 #'number-to-string
-			 (org-export-get-headline-number headline info)
-			 "-")))))
+	    (format "\\label{%s}\n" (org-export-get-headline-id headline info)))
 	   (pre-blanks
 	    (make-string (org-element-property :pre-blank headline) 10)))
       (if (or (not section-fmt) (org-export-low-level-p headline info))
@@ -1971,17 +1963,7 @@ INFO is a plist holding contextual information.  See
 	  ;; number.  Otherwise, display description or headline's
 	  ;; title.
 	  (headline
-	   (let* ((custom-label
-		   (and (plist-get info :latex-custom-id-labels)
-			(org-element-property :CUSTOM_ID destination)))
-		  (label
-		   (or
-		    custom-label
-		    (format "sec-%s"
-			    (mapconcat
-			     #'number-to-string
-			     (org-export-get-headline-number destination info)
-			     "-")))))
+	   (let ((label (org-export-get-headline-id link info)))
 	     (if (and (plist-get info :section-numbers) (not desc))
 		 (format "\\ref{%s}" label)
 	       (format "\\hyperref[%s]{%s}" label
diff --git a/lisp/ox-md.el b/lisp/ox-md.el
index 695fb61..383c629 100644
--- a/lisp/ox-md.el
+++ b/lisp/ox-md.el
@@ -202,12 +202,7 @@ a communication channel."
 		   (and char (format "[#%c] " char)))))
 	   (anchor
 	    (when (plist-get info :with-toc)
-	      (org-html--anchor
-	       (or (org-element-property :CUSTOM_ID headline)
-		   (concat "sec-"
-			   (mapconcat 'number-to-string
-				      (org-export-get-headline-number
-				       headline info) "-")))
+	      (org-html--anchor (org-export-get-headline-id headline info)
 	       nil nil info)))
 	   ;; Headline text without tags.
 	   (heading (concat todo priority title))
@@ -330,10 +325,12 @@ a communication channel."
 	   (and contents (concat contents " "))
 	   (format "(%s)"
 		   (format (org-export-translate "See section %s" :html info)
-			   (mapconcat 'number-to-string
-				      (org-export-get-headline-number
-				       destination info)
-				      ".")))))))
+			   (if (org-export-numbered-headline-p destination info)
+			       (mapconcat 'number-to-string
+					  (org-export-get-headline-number
+					   destination info)
+					  ".")
+			     (org-export-get-alt-title headline info))))))))
      ((org-export-inline-image-p link org-html-inline-image-rules)
       (let ((path (let ((raw-path (org-element-property :path link)))
 		    (if (not (file-name-absolute-p raw-path)) raw-path
@@ -354,9 +351,13 @@ a communication channel."
 	(if (org-string-nw-p contents) contents
 	  (when destination
 	    (let ((number (org-export-get-ordinal destination info)))
-	      (when number
+	      (if number
 		(if (atom number) (number-to-string number)
-		  (mapconcat 'number-to-string number "."))))))))
+		  (mapconcat 'number-to-string number "."))
+		;; unnumbered headline
+		(when (equal 'headline (org-element-type destination))
+		  ;; BUG: shouldn't headlines have a form like [ref](name) in md
+		  (org-export-data (org-export-get-alt-title destination info) info))))))))
      ;; Link type is handled by a special function.
      ((let ((protocol (nth 2 (assoc type org-link-protocols))))
 	(and (functionp protocol)
diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
index 96a3b83..d5498ec 100644
--- a/lisp/ox-odt.el
+++ b/lisp/ox-odt.el
@@ -1816,12 +1816,15 @@ holding contextual information."
 	   ;; Get level relative to current parsed data.
 	   (level (org-export-get-relative-level headline info))
 	   ;; Get canonical label for the headline.
-	   (id (concat "sec-" (mapconcat 'number-to-string
-					 (org-export-get-headline-number
-					  headline info) "-")))
+	   (id (org-export-get-headline-id headline info))
 	   ;; Get user-specified labels for the headline.
-	   (extra-ids (list (org-element-property :CUSTOM_ID headline)
-			    (org-element-property :ID headline)))
+	   (extra-ids (delq id (list
+				(org-element-property :CUSTOM_ID headline)
+				(org-element-property :ID headline)
+				(when (org-export-numbered-headline-p headline info)
+				  (concat "sec-" (mapconcat 'number-to-string
+							    (org-export-get-headline-number
+							     headline info) "-"))))))
 	   ;; Extra targets.
 	   (extra-targets
 	    (mapconcat (lambda (x)
@@ -1870,9 +1873,13 @@ holding contextual information."
        (t
 	(concat
 	 (format
-	  "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\">%s</text:h>"
+	  "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\" text:is-list-header=\"%s\">%s</text:h>"
 	  (format "Heading_20_%s" level)
 	  level
+	  ;; text:is-list-header is how LO calls an unnumbered headline
+	  ;; however, the definition here sounds weird:
+	  ;; http://docs.oasis-open.org/office/v1.2/cs01/OpenDocument-v1.2-cs01-part1.html#__RefHeading__1415152_253892949
+	  (if (org-export-numbered-headline-p headline info) "false" "true")
 	  (concat extra-targets anchored-title))
 	 contents))))))
 
@@ -2643,10 +2650,7 @@ Return nil, otherwise."
   (let* ((genealogy (org-export-get-genealogy destination))
 	 (data (reverse genealogy))
 	 (label (case (org-element-type destination)
-		  (headline
-		   (format "sec-%s" (mapconcat 'number-to-string
-					       (org-export-get-headline-number
-						destination info) "-")))
+		  (headline (org-export-get-headline-id destination info))
 		  (target
 		   (org-element-property :value destination))
 		  (t (error "FIXME: Resolve %S" destination)))))
@@ -2777,10 +2781,10 @@ INFO is a plist holding contextual information.  See
 	   ;; Otherwise, try to provide a meaningful description.
 	   (if (not desc) (org-odt-link--infer-description destination info)
 	     (let* ((headline-no
-		     (org-export-get-headline-number destination info))
-		    (label
-		     (format "sec-%s"
-			     (mapconcat 'number-to-string headline-no "-"))))
+		     (if (org-export-numbered-headline-p destination info)
+			 (org-export-get-headline-number destination info)
+		       (org-export-get-alt-title destination info)))
+		    (label (org-export-get-headline-id destination info)))
 	       (format
 		"<text:a xlink:type=\"simple\" xlink:href=\"#%s\">%s</text:a>"
 		label desc))))
diff --git a/lisp/ox.el b/lisp/ox.el
index f01f951..55c02eb 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -2003,7 +2003,8 @@ for a footnotes section."
   (let ((numbering (make-vector org-export-max-depth 0)))
     (org-element-map data 'headline
       (lambda (headline)
-	(unless (org-element-property :footnote-section-p headline)
+	(unless (or (org-element-property :footnote-section-p headline)
+		    (not (org-export-numbered-headline-p headline options)))
 	  (let ((relative-level
 		 (1- (org-export-get-relative-level headline options))))
 	    (cons
@@ -3807,6 +3808,40 @@ and the last level being considered as high enough, or nil."
       (let ((level (org-export-get-relative-level headline info)))
         (and (> level limit) (- level limit))))))
 
+(defun org-export-get-headline-id (headline info)
+  "Return a unique ID for HEADLINE.
+INFO is a plist holding contextual information.
+
+The method of generating the unique ID is as follows.
+ID is the first matching non-nil value of the following list:
+1. The CUSTOM_ID property.
+2. A relative level number if the headline is numbered.
+3. The ID property
+4. A new generated unique ID."
+  ;; FIX: this is seemingly incompatible (??) with
+  ;; `org-latex-custom-id-as-label'.  However, it seems *no* similar
+  ;; variable exists for ox-html.  Can be get drop `org-latex-custom-id-as-label'?
+  ;; Causal note: I have never touched this variable and yet custom_id works as expected.
+  ;; TODO: Better way is to check that CUSTOM_ID is unique?  Or is that too intrusive?
+  (cond
+   ;; test if CUSTOM_ID exists and is unique
+   ((and (org-element-property :CUSTOM_ID headline)
+	 (zerop (1- (length (org-element-map (plist-get info :parse-tree) 'headline
+			      (lambda (otherhead)
+				(equal (org-element-property :CUSTOM_ID headline)
+				       (org-element-property :CUSTOM_ID otherhead))))))))
+    (org-element-property :CUSTOM_ID headline))
+   ;; test if numbered and return sec-num string
+   ((org-export-numbered-headline-p headline info)
+    (format "sec-%s"
+	    (mapconcat #'number-to-string
+		       (org-export-get-headline-number headline info) "-")))
+   ;; return :ID and assign if necessary.  Lasts for one export.
+   (t (and (or (org-element-property :ID headline)
+	       (org-element-put-property
+		headline :ID (replace-regexp-in-string ":" "-" (org-id-new "sec"))))
+	   (org-element-property :ID headline)))))
+
 (defun org-export-get-headline-number (headline info)
   "Return HEADLINE numbering as a list of numbers.
 INFO is a plist holding contextual information."
@@ -3815,9 +3850,10 @@ INFO is a plist holding contextual information."
 (defun org-export-numbered-headline-p (headline info)
   "Return a non-nil value if HEADLINE element should be numbered.
 INFO is a plist used as a communication channel."
-  (let ((sec-num (plist-get info :section-numbers))
-	(level (org-export-get-relative-level headline info)))
-    (if (wholenump sec-num) (<= level sec-num) sec-num)))
+  (unless (org-export-get-node-property :UNNUMBERED headline t)
+      (let ((sec-num (plist-get info :section-numbers))
+		 (level (org-export-get-relative-level headline info)))
+	     (if (wholenump sec-num) (<= level sec-num) sec-num)))))
 
 (defun org-export-number-to-roman (n)
   "Convert integer N into a roman numeral."
-- 
2.1.0

Reply via email to