From fb44a552a151025513b645527498325febb6118f Mon Sep 17 00:00:00 2001 From: Rasmus 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%s" + "\n%s" (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 "%s" 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