* [patch, ox] Unnumbered headlines @ 2014-08-08 13:39 Rasmus 2014-08-08 22:35 ` Alan L Tyree ` (3 more replies) 0 siblings, 4 replies; 27+ messages in thread From: Rasmus @ 2014-08-08 13:39 UTC (permalink / raw) To: emacs-orgmode; +Cc: alantyree, tsd [-- Attachment #1: Type: text/plain, Size: 1991 bytes --] Hi, In a recent thread¹ Tom and Alan mention that authors sometimes need unnumbered headlines, e.g. for prefaces. This patch (tries to) add this feature via the tag :nonumber: (customizable via Custom or in-file). I make two assumptions. First, the tag is recursive, so if the parent is not numbered the child is not numbered. Secondly, I depart from the LaTeX tradition of ignoring unnumbered headlines in the TOC (except in the case of ox-latex.el where it depends on org-latex-classes). (See example below). Needless to say such a feature needs to be discussed and I not sure whether the greater Org community finds it useful or needless clutter. In my opinion a :nonumber: tag is a natural continuation of :export: and :noexport: and unlike :ignoreheading: the implementation is fairly clean (or maybe I'm cheating myself here). A reason for why to include it is that it seems relatively easy to do *during* export, but it's hard to consistently get it right on in both headlines and the TOC via filters. The patch is messing with ox.el, and thus I would appreciate a review and potentially testing, in the case that it is agreed that such a feature would be OK to add to ox. It seems to work well with ox-latex.el, ox-ascii.el and ox-html.el. It doesn't play well with ox-odt.el (headlines are still numbered). I will fix this as well as adding documentation if a consensus of the worthwhileness of the patch can be reached. Finally, here's an example output using ox-ascii #+begin_src org * a (not numbered) :nonum: ** aa (not numbert) * b (1) ** ba (not numbered) :nonum: *** baa (not numbered) ** bb (1.1) #+end_src #+RESULTS: (TOC only, but the rest is as expected) a (not numbered) .. aa (not numbert) 1 b (1) .. ba (not numbered) ..... baa (not numbered) .. 1.1 bb (1.1) Thanks, Rasmus Footnotes: ¹ http://permalink.gmane.org/gmane.emacs.orgmode/89515 -- Vote for proprietary math! [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-ox.el-Support-unnumbered-headlines-via-tag.patch --] [-- Type: text/x-diff, Size: 5626 bytes --] From d38a728fef66af9df2a0b87e2126533961d87ecf Mon Sep 17 00:00:00 2001 From: Rasmus <rasmus@gmx.us> Date: Fri, 8 Aug 2014 14:53:01 +0200 Subject: [PATCH] ox.el: Support unnumbered headlines via tag. * ox.el (org-export-options-alist): NO_NUMBER_TAGS new keyword. (org-export-not-numbered-tags): New defcustom. (org-export--collect-headline-numbering): Considers whether headline is numbered. (org-export-numbered-headline-p): Tests if headline is to be numbered. (org-export--recursive-tags): New function based. Previouesly part of `orge-export-get-tags'. (org-export-get-tags): Ignoes NO_NUMBER_TAGS. --- lisp/ox.el | 60 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/lisp/ox.el b/lisp/ox.el index d47c2e6..2fff14f 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -109,6 +109,7 @@ (:language "LANGUAGE" nil org-export-default-language t) (:select-tags "SELECT_TAGS" nil org-export-select-tags split) (:exclude-tags "EXCLUDE_TAGS" nil org-export-exclude-tags split) + (:no-number-tags "NO_NUMBER_TAGS" nil org-export-not-numbered-tags split) (:creator "CREATOR" nil org-export-creator-string) (:headline-levels nil "H" org-export-headline-levels) (:preserve-breaks nil "\\n" org-export-preserve-breaks) @@ -448,6 +449,18 @@ e.g. \"*:nil\"." :group 'org-export-general :type 'boolean) + +(defcustom org-export-not-numbered-tags '("nonumber") + "Tags that exclude trees from obtaining numbers. + +All trees carrying any of these tags will be excluded from +receiving a number. This includes subtress. + +This option can also be set in files with the NO_NUMBER_TAGS +keyword." + :group 'org-export-general + :type '(repeat (string :tag "Tag"))) + (defcustom org-export-exclude-tags '("noexport") "Tags that exclude a tree from export. @@ -1993,7 +2006,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 info))) (let ((relative-level (1- (org-export-get-relative-level headline options)))) (cons @@ -3785,9 +3799,12 @@ 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)) + (let ((tags (org-export--recursive-tags headline info)) + (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 (loop for k in (plist-get info :no-number-tags) + thereis (member k tags)) + (if (wholenump sec-num) (<= level sec-num) sec-num)))) (defun org-export-number-to-roman (n) "Convert integer N into a roman numeral." @@ -3805,14 +3822,33 @@ INFO is a plist used as a communication channel." (pop roman))) res))) +(defun org-export--recursive-tags (element info) + "Return the full list of recursive tags associated with ELEMENT. + +ELEMENT has either an `headline' or an `inlinetask' type. INFO +is a plist used as a communication channel." + (let ((current-tag-list (org-element-property :tags element))) + (mapc + (lambda (parent) + (mapc + (lambda (tag) + (when (and (memq (org-element-type parent) '(headline inlinetask)) + (not (member tag current-tag-list))) + (push tag current-tag-list))) + (org-element-property :tags parent))) + (org-export-get-genealogy element)) + ;; Add FILETAGS keywords and return results. + (org-uniquify (append (plist-get info :filetags) current-tag-list)))) + (defun org-export-get-tags (element info &optional tags inherited) "Return list of tags associated to ELEMENT. ELEMENT has either an `headline' or an `inlinetask' type. INFO is a plist used as a communication channel. -Select tags (see `org-export-select-tags') and exclude tags (see -`org-export-exclude-tags') are removed from the list. +Select tags (see `org-export-select-tags'), exclude tags (see +`org-export-exclude-tags') and no-number tags (see +`org-export-no-number-tags') are removed from the list. When non-nil, optional argument TAGS should be a list of strings. Any tag belonging to this list will also be removed. @@ -3822,21 +3858,11 @@ inherited from parent headlines and FILETAGS keywords." (org-remove-if (lambda (tag) (or (member tag (plist-get info :select-tags)) (member tag (plist-get info :exclude-tags)) + (member tag (plist-get info :no-number-tags)) (member tag tags))) (if (not inherited) (org-element-property :tags element) ;; Build complete list of inherited tags. - (let ((current-tag-list (org-element-property :tags element))) - (mapc - (lambda (parent) - (mapc - (lambda (tag) - (when (and (memq (org-element-type parent) '(headline inlinetask)) - (not (member tag current-tag-list))) - (push tag current-tag-list))) - (org-element-property :tags parent))) - (org-export-get-genealogy element)) - ;; Add FILETAGS keywords and return results. - (org-uniquify (append (plist-get info :filetags) current-tag-list)))))) + (org-export--recursive-tags (element info))))) (defun org-export-get-node-property (property blob &optional inherited) "Return node PROPERTY value for BLOB. -- 2.0.4 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-08-08 13:39 [patch, ox] Unnumbered headlines Rasmus @ 2014-08-08 22:35 ` Alan L Tyree 2014-08-09 1:04 ` [patch, ox] Unnumbered headlines - early test Alan L Tyree ` (2 subsequent siblings) 3 siblings, 0 replies; 27+ messages in thread From: Alan L Tyree @ 2014-08-08 22:35 UTC (permalink / raw) To: emacs-orgmode; +Cc: rasmus On 08/08/14 23:39, Rasmus wrote: > Hi, > > In a recent thread¹ Tom and Alan mention that authors sometimes need > unnumbered headlines, e.g. for prefaces. This patch (tries to) add > this feature via the tag :nonumber: (customizable via Custom or > in-file). > > I make two assumptions. First, the tag is recursive, so if the parent > is not numbered the child is not numbered. Secondly, I depart from > the LaTeX tradition of ignoring unnumbered headlines in the TOC > (except in the case of ox-latex.el where it depends on > org-latex-classes). (See example below). > > Needless to say such a feature needs to be discussed and I not sure > whether the greater Org community finds it useful or needless clutter. > > In my opinion a :nonumber: tag is a natural continuation of :export: > and :noexport: and unlike :ignoreheading: the implementation is fairly > clean (or maybe I'm cheating myself here). A reason for why to > include it is that it seems relatively easy to do *during* export, but > it's hard to consistently get it right on in both headlines and the > TOC via filters. > > The patch is messing with ox.el, and thus I would appreciate a review > and potentially testing, in the case that it is agreed that such a > feature would be OK to add to ox. > > It seems to work well with ox-latex.el, ox-ascii.el and ox-html.el. > It doesn't play well with ox-odt.el (headlines are still numbered). I > will fix this as well as adding documentation if a consensus of the > worthwhileness of the patch can be reached. > > Finally, here's an example output using ox-ascii > > #+begin_src org > * a (not numbered) :nonum: > ** aa (not numbert) > * b (1) > ** ba (not numbered) :nonum: > *** baa (not numbered) > ** bb (1.1) > > #+end_src > > #+RESULTS: (TOC only, but the rest is as expected) > a (not numbered) > .. aa (not numbert) > 1 b (1) > .. ba (not numbered) > ..... baa (not numbered) > .. 1.1 bb (1.1) > > > Thanks, > Rasmus > > Footnotes: > ¹ http://permalink.gmane.org/gmane.emacs.orgmode/89515 > > -- > Vote for proprietary math! Rasmus, you're my hero! Regarding the two assumptions: - Recursive tags: I think this is correct. I don't think it matters too much for my use case since things like the Preface will ordinarily be top level headlines and unlikely to have children. If there are child headlines, then I don't see why numbering would be required. - Table of contents: I'm sure this is correct. I always ended up adding to the TOC when using LaTeX anyway. The frontmatter of a book has two distinct types of pages: - title pages, copyright pages and so forth. If these pages are headlined at all, then the :ignore: tag and Eric's filter takes care of them; - things like the Preface, Forward and (in my case) Table of Statutes and Table of cases. This type wants to be referenced in the TOC but they definitely do not want to be sequentially numbered as chapters. The Wikipedia entry on Book Design lists 12 types of frontmatter pages: http://en.wikipedia.org/wiki/Book_design. It's easy to see which ones fit into which category. I think this facility will *greatly* enhance org-mode for book authors/publishers. It will certainly make the conversion to ePub go more smoothly. Cheers, Alan -- Alan L Tyree http://www2.austlii.edu.au/~alan Tel: 04 2748 6206 sip:typhoon@iptel.org ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines - early test 2014-08-08 13:39 [patch, ox] Unnumbered headlines Rasmus 2014-08-08 22:35 ` Alan L Tyree @ 2014-08-09 1:04 ` Alan L Tyree 2014-08-09 7:47 ` [patch, ox] Unnumbered headlines Detlef Steuer 2014-08-11 14:18 ` Nicolas Goaziou 3 siblings, 0 replies; 27+ messages in thread From: Alan L Tyree @ 2014-08-09 1:04 UTC (permalink / raw) To: emacs-orgmode I have a book length MS that I tested the patch on. * Copyright page :nonumber: * Preface :nonumber: * Law relating to sale of goods ....... etc Export looked good and as expected, that is, no numbers on the first two headlines and the third headline numbered 1. as it should be. Table of contents was as expected. LaTeX and ascii exports also looked great. However, running "tidy -m sog.html" on the resulting file threw up the following warnings: line 222 column 1 - Warning: <div> anchor "outline-container-sec-" already defined line 223 column 1 - Warning: <h2> anchor "sec-" already defined line 224 column 1 - Warning: <div> anchor "text-" already defined Info: Doctype given is "-//W3C//DTD XHTML 1.0 Strict//EN" Info: Document content looks like XHTML 1.0 Strict 3 warnings, 0 errors were found! Line 222, 223 and 224 relate to the Preface heading. The offending items were, of course, copies of the corresponding items in Copyright page. To avoid these, I think you need to give unique id and sec markers to the unnumbered headlines. It matters because the resulting ePub will not validate unless the html passes the "tidy test". Cheers, Alan -- Alan L Tyree http://www2.austlii.edu.au/~alan Tel: 04 2748 6206 sip:typhoon@iptel.org ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-08-08 13:39 [patch, ox] Unnumbered headlines Rasmus 2014-08-08 22:35 ` Alan L Tyree 2014-08-09 1:04 ` [patch, ox] Unnumbered headlines - early test Alan L Tyree @ 2014-08-09 7:47 ` Detlef Steuer 2014-08-11 14:18 ` Nicolas Goaziou 3 siblings, 0 replies; 27+ messages in thread From: Detlef Steuer @ 2014-08-09 7:47 UTC (permalink / raw) To: emacs-orgmode Am Fri, 08 Aug 2014 15:39:15 +0200 schrieb Rasmus <rasmus@gmx.us>: > Needless to say such a feature needs to be discussed and I not sure > whether the greater Org community finds it useful or needless clutter. Definitly a candidate for inclusion in org-mode. Thx for the patch! Detlef ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-08-08 13:39 [patch, ox] Unnumbered headlines Rasmus ` (2 preceding siblings ...) 2014-08-09 7:47 ` [patch, ox] Unnumbered headlines Detlef Steuer @ 2014-08-11 14:18 ` Nicolas Goaziou 2014-08-11 15:37 ` Rasmus 3 siblings, 1 reply; 27+ messages in thread From: Nicolas Goaziou @ 2014-08-11 14:18 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree, tsd Hello, Rasmus <rasmus@gmx.us> writes: > In a recent thread¹ Tom and Alan mention that authors sometimes need > unnumbered headlines, e.g. for prefaces. This patch (tries to) add > this feature via the tag :nonumber: (customizable via Custom or > in-file). Interesting. Some comments follow. > I make two assumptions. First, the tag is recursive, so if the parent > is not numbered the child is not numbered. Indeed. > Secondly, I depart from the LaTeX tradition of ignoring unnumbered > headlines in the TOC (except in the case of ox-latex.el where it > depends on org-latex-classes). (See example below). OK. > In my opinion a :nonumber: tag is a natural continuation of :export: > and :noexport: First, maybe a tag is not the best way to specify it. Tags are rather obnoxious and their length is somewhat limited by the width of the window. Another option is to use properties, e.g. "UNNUMBERED", or "NO_NUMBER" with a non-nil value * Some headline :PROPERTIES: :UNNUMBERED: t :END: It is harder to notice an unnumbered headline, but it doesn't add cruft to the tag line, and this is far less important than :noexport:. This is not perfect either, but I think the trade-off is honest. Another advantage is inheritance is already implemented for node properties (see `org-export-get-node-property'). > and unlike :ignoreheading: the implementation is fairly clean (or > maybe I'm cheating myself here). Do not underestimate it: implementing this feature is a bit tricky, and will introduce backward incompatible changes to export back-ends (possibly outside the scope of core+contrib). A major problem comes from `org-export-get-headline-number', which always returns a unique non-nil value for headlines in a parse tree. Even unnumbered headlines (e.g. with option num:2) get a number. Consequently, some back-ends use this number as a unique ID, as this excerpt from "ox-html.el" (format "<span class=\"section-number-%d\">%s</span> " level (mapconcat #'number-to-string numbers ".")) It is not possible to rely on this mechanism with your patch. A solution is to create two functions for the two features: - `org-export-get-headline-number' :: the same as today, but returns nil if headline is unnumbered. - `org-export-get-headline-id' :: returns a unique ID, as an integer, for the current headline, notwithstanding its numbering status. Then `org-export-get-ordinal' should probably try to call the first one and fallback to the second one. The previous snippet from "ox-html.el" would become (format "<span class=\"section-number-%d\">%d</span> " level (org-export-get-headline-id headline info)) Obviously, this implies that every back-end using this construct should be updated accordingly. WDYT? Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-08-11 14:18 ` Nicolas Goaziou @ 2014-08-11 15:37 ` Rasmus 2014-08-12 8:58 ` Nicolas Goaziou 0 siblings, 1 reply; 27+ messages in thread From: Rasmus @ 2014-08-11 15:37 UTC (permalink / raw) To: emacs-orgmode; +Cc: alantyree, tsd Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: > Hello, > > Rasmus <rasmus@gmx.us> writes: > >> In a recent thread¹ Tom and Alan mention that authors sometimes need >> unnumbered headlines, e.g. for prefaces. This patch (tries to) add >> this feature via the tag :nonumber: (customizable via Custom or >> in-file). > > Interesting. Some comments follow. > >> I make two assumptions. First, the tag is recursive, so if the parent >> is not numbered the child is not numbered. > > Indeed. > >> Secondly, I depart from the LaTeX tradition of ignoring unnumbered >> headlines in the TOC (except in the case of ox-latex.el where it >> depends on org-latex-classes). (See example below). > > OK. The TOC part can easily be added, of course, but it's second order at the moment. >> In my opinion a :nonumber: tag is a natural continuation of :export: >> and :noexport: > > First, maybe a tag is not the best way to specify it. Tags are rather > obnoxious and their length is somewhat limited by the width of the > window. True. Personally, I find them nicer as you can just C-c C-c on your headline and write down some keyword that is typically easy to remember. For properties I must rely on C-c C-x p — since the syntax is so awkward — and while it may be memorable to me try to explain it to co-authors who are not long-time Emacs users. > Another option is to use properties, e.g. "UNNUMBERED", or "NO_NUMBER" > with a non-nil value > > * Some headline > :PROPERTIES: > :UNNUMBERED: t > :END: > > It is harder to notice an unnumbered headline, but it doesn't add cruft > to the tag line, and this is far less important than :noexport:. This is > not perfect either, but I think the trade-off is honest. I can see you point, and I think I agree, though I personally much, much prefer tags. Of course a utility function could be added (like the beamer minor mode) that adds the tag to give you a visual clue while the properties are really what matter. > Another advantage is inheritance is already implemented for node > properties (see `org-export-get-node-property'). Interesting. I did not know. >> and unlike :ignoreheading: the implementation is fairly clean (or >> maybe I'm cheating myself here). > > Do not underestimate it: implementing this feature is a bit tricky, and > will introduce backward incompatible changes to export back-ends > (possibly outside the scope of core+contrib). > > A major problem comes from `org-export-get-headline-number', which > always returns a unique non-nil value for headlines in a parse tree. > Even unnumbered headlines (e.g. with option num:2) get a number. > Consequently, some back-ends use this number as a unique ID, as this > excerpt from "ox-html.el" > > (format "<span class=\"section-number-%d\">%s</span> " > level > (mapconcat #'number-to-string numbers ".")) > > It is not possible to rely on this mechanism with your patch. > > A solution is to create two functions for the two features: > > - `org-export-get-headline-number' :: the same as today, but returns > nil if headline is unnumbered. > > - `org-export-get-headline-id' :: returns a unique ID, as an integer, > for the current headline, notwithstanding its numbering status. > > Then `org-export-get-ordinal' should probably try to call the first one > and fallback to the second one. In my local branch (not published; still problems with ox-html) I "solve" this by retaining a number for unnumbered headlines and having parallel numbering. That is, there may exists a numbered section X.y as well as a unnumbered section X.y when applicable. Of course there is still an issue in that there is no way of differentiating the a number list from a numbered section and an unnumbered one and consequently e.g. ox-html IDs will still be broken. The most elegant way would perhaps be to introduce in the output of `org-export-get-headline-number' whether a headline is unnumbered, but I am not sure how to do this without breaking the expected output of the function. A dirty fix might be add an extra 0 to the beginning of the section number list when dealing with unnumbered headings, but it is not so nice as the numbers loose their meaning. . . What I am currently trying to do with ox-html is combining `org-export-get-headline-number' (which is always a list of numbers) and `org-export-numbered-headline-p' to determine if the ID should contain the substring "unnumbered". Then the first unnumbered section would be "sec-unnumbered-1" and so forth. > The previous snippet from "ox-html.el" would become > > (format "<span class=\"section-number-%d\">%d</span> " > level > (org-export-get-headline-id headline info)) What I was experiment with is something like: (format "<span class=\"section-number-%s-%d\">%d</span> " (if numberedp "" "unumbered") level) Do you think it would be better to work on `org-export-get-headline-id' and "solve" the issue in that way is a better approach than what I describe above? > Obviously, this implies that every back-end using this construct should > be updated accordingly. Thanks, Rasmus -- Bang bang ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-08-11 15:37 ` Rasmus @ 2014-08-12 8:58 ` Nicolas Goaziou 2014-09-20 16:02 ` Rasmus 0 siblings, 1 reply; 27+ messages in thread From: Nicolas Goaziou @ 2014-08-12 8:58 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree, tsd Hello, Rasmus <rasmus@gmx.us> writes: > True. Personally, I find them nicer as you can just C-c C-c on your > headline and write down some keyword that is typically easy to > remember. For properties I must rely on C-c C-x p — since the syntax > is so awkward — and while it may be memorable to me try to explain it > to co-authors who are not long-time Emacs users. > >> Another option is to use properties, e.g. "UNNUMBERED", or "NO_NUMBER" >> with a non-nil value >> >> * Some headline >> :PROPERTIES: >> :UNNUMBERED: t >> :END: >> >> It is harder to notice an unnumbered headline, but it doesn't add cruft >> to the tag line, and this is far less important than :noexport:. This is >> not perfect either, but I think the trade-off is honest. > > I can see you point, and I think I agree, though I personally much, > much prefer tags. Of course, tags are easier to use than properties, hence their popularity. But they have some limitations, too. I'm not saying that we must use a property, but this is definitely something to ponder. > Of course a utility function could be added (like the beamer minor > mode) that adds the tag to give you a visual clue while the properties > are really what matter. Which would defeat the choice of using a property (i.e. avoid visual clutter). >> Another advantage is inheritance is already implemented for node >> properties (see `org-export-get-node-property'). > > Interesting. I did not know. Actually, it's not an "advantage" per se, since `org-export-get-tags' does it too. > The most elegant way would perhaps be to introduce in the output of > `org-export-get-headline-number' whether a headline is unnumbered, but > I am not sure how to do this without breaking the expected output of > the function. > > A dirty fix might be add an extra 0 to the beginning of the section > number list when dealing with unnumbered headings, but it is not so > nice as the numbers loose their meaning. . . Also, headline numbers can start with 0: ** H0.1 * H1 ** H1.1 You could add a 0 at the end instead, or begin with a negative number, which cannot happen otherwise, but that would be hackish, for sure. Since "ox-html.el", and possibly other back-ends, rely on `number-to-string', there not much else to do down this road. > What I am currently trying to do with ox-html is combining > `org-export-get-headline-number' (which is always a list of numbers) > and `org-export-numbered-headline-p' to determine if the ID should > contain the substring "unnumbered". Then the first unnumbered section > would be "sec-unnumbered-1" and so forth. > >> The previous snippet from "ox-html.el" would become >> >> (format "<span class=\"section-number-%d\">%d</span> " >> level >> (org-export-get-headline-id headline info)) > > What I was experiment with is something like: > > (format "<span class=\"section-number-%s-%d\">%d</span> " > (if numberedp "" "unumbered") > level) > > Do you think it would be better to work on > `org-export-get-headline-id' and "solve" the issue in that way is a > better approach than what I describe above? Roughly, both approaches require the same amount of changes and imply the same incompatibilities. Nevertheless, I think it is cleaner for `org-export-get-headline-number' to return nil when a headline is not numbered and to separate both intents, i.e, provide a number and a unique internal id. Anyway, feel free to experiment, there's no hurry. Thanks for your work. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-08-12 8:58 ` Nicolas Goaziou @ 2014-09-20 16:02 ` Rasmus 2014-09-20 20:34 ` Alan L Tyree 2014-09-21 13:12 ` Nicolas Goaziou 0 siblings, 2 replies; 27+ messages in thread From: Rasmus @ 2014-09-20 16:02 UTC (permalink / raw) To: emacs-orgmode; +Cc: alantyree, tsd [-- Attachment #1: Type: text/plain, Size: 1699 bytes --] 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 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-ox-Support-unnumbered-headlines-via-property.patch --] [-- Type: text/x-diff, Size: 14288 bytes --] From fb44a552a151025513b645527498325febb6118f Mon Sep 17 00:00:00 2001 From: Rasmus <rasmus@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 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-20 16:02 ` Rasmus @ 2014-09-20 20:34 ` Alan L Tyree 2014-09-21 13:12 ` Nicolas Goaziou 1 sibling, 0 replies; 27+ messages in thread From: Alan L Tyree @ 2014-09-20 20:34 UTC (permalink / raw) To: Rasmus, emacs-orgmode; +Cc: tsd Thanks very much for working on this, Rasmus. I'll try to test it out over the next couple of days. It really will make book production much nicer! Cheers, Alan On 21/09/14 02:02, Rasmus wrote: > 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)"? > > -- Alan L Tyree http://www2.austlii.edu.au/~alan Tel: 04 2748 6206 sip:typhoon@iptel.org ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-20 16:02 ` Rasmus 2014-09-20 20:34 ` Alan L Tyree @ 2014-09-21 13:12 ` Nicolas Goaziou 2014-09-21 14:37 ` Rasmus 1 sibling, 1 reply; 27+ messages in thread From: Nicolas Goaziou @ 2014-09-21 13:12 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree, tsd Hello, Rasmus <rasmus@gmx.us> writes: > 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. Thanks for that work. Some comments follow. > 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. Sounds good. > 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. I think we should keep separated CUSTOM_ID and ID on the one hand, and internal reference on the other hand. There's no guarantee that CUSTOM_ID will be unique, and, even if you check it out, there's no guarantee either that its value can be used as-is in the generated docstring (think about ":CUSTOM_ID: 100%" in LaTeX export). CUSTOM_ID is a human readable reference to an Org headline. This headline should be referred to internally with a more specific id. To a lesser extent, I think this also applied to org-id. Also `org-export-headline-internal-id' (or some such, to avoid confusion with other IDs) may return a value simpler than what `org-id-new' returns (e.g. a dumb counter). If it is possible, the output will be easier to read. > Anyhow, `org-export-get-headline-id' ensures that we can refer to > unnumbered headlines, which was not possible before. This is untrue. > Of course, in LaTeX such ref to a \section* will be nonsense, so we > could introduce a \pageref here. At the moment, referring to an unnumbered section displays its name. > 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). This variable has its use if custom-id and internal representation are separated. > 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)"? There is probably something to do here, but that would be in another patch. > * 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. You can also write "Support unnumbered headlines" just once. > + (when (equal 'headline (org-element-type destination)) Comparing symbols with `equal' is a sin beyond redemption. Use `eq'. > + (format "[%s]" (org-export-data (org-export-get-alt-title destination info) info))))))))) alt title is for table of contents only. > (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 "-"))) Nitpick: I suggest `and' instead of `when' since there's no side-effect. > + (when section-number (concat "sec-" section-number)) Ditto. > + (unless (org-export-get-node-property :UNNUMBERED headline t) Actually, this test is fragile. In the following example * H1 :PROPERTIES: :UNNUMBERED: t :END: ** H2 :PROPERTIES: :UNNUMBERED: nil :END: *** H3 H3 should clearly be unnumbered even though inheritance disagrees. This is a bit stronger than inheritance: if there is a single non-nil UNNUMBERED property among ancestors (or the headline itself), it cannot be numbered. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-21 13:12 ` Nicolas Goaziou @ 2014-09-21 14:37 ` Rasmus 2014-09-21 19:40 ` Nicolas Goaziou 0 siblings, 1 reply; 27+ messages in thread From: Rasmus @ 2014-09-21 14:37 UTC (permalink / raw) To: mail, emacs-orgmode; +Cc: alantyree, tsd Hi, Thanks for the comments. Let's give it another try, shand't we. Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: >> 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. > > I think we should keep separated CUSTOM_ID and ID on the one hand, and > internal reference on the other hand. > > There's no guarantee that CUSTOM_ID will be unique, and, even if you > check it out, there's no guarantee either that its value can be used > as-is in the generated docstring (think about ":CUSTOM_ID: 100%" in > LaTeX export). > > CUSTOM_ID is a human readable reference to an Org headline. This > headline should be referred to internally with a more specific id. To > a lesser extent, I think this also applied to org-id. > > Also `org-export-headline-internal-id' (or some such, to avoid confusion > with other IDs) may return a value simpler than what `org-id-new' > returns (e.g. a dumb counter). If it is possible, the output will be > easier to read. Okay, I returned to my first hack (which never made it to this list). Basically, I ID everything. Unnumbered sections get the id "unnumbered-sec-COUNTER" and numbered sections get the id "sec-COUNTER". Perhaps you will find it too much of a hack. The other easy alternative, which I find less pleasing, would have on incremental counter, but it makes it harder to read. A third alternative is give the numbers like in this patch, but store the numbers for unnumbered section somewhere else than :headline-numbering. >> Anyhow, `org-export-get-headline-id' ensures that we can refer to >> unnumbered headlines, which was not possible before. > > This is untrue. > >> Of course, in LaTeX such ref to a \section* will be nonsense, so we >> could introduce a \pageref here. > > At the moment, referring to an unnumbered section displays its name. In some modes, yes. In LaTeX it produces a \ref{·} that LaTeX will laugh at. If you have a better idea than using the title I'm all ears! >> + (when (equal 'headline (org-element-type destination)) > > Comparing symbols with `equal' is a sin beyond redemption. Use `eq'. Shiiit. . . Why, out of curiosity? I though equal was like the meaner, tougher eq, that gets shit right, but is a bit more expensive. >> + (format "[%s]" (org-export-data (org-export-get-alt-title >> destination info) info))))))))) > > alt title is for table of contents only. Yeah, but I though, conditional on using title for ref when nothing better exists, it would be nicer to have something shorter. Anyway, I changed it to plain title now. > Nitpick: I suggest `and' instead of `when' since there's no side-effect. OK, I think I got 'em all. >> + (unless (org-export-get-node-property :UNNUMBERED headline t) > > Actually, this test is fragile. In the following example > > * H1 > :PROPERTIES: > :UNNUMBERED: t > :END: > ** H2 > :PROPERTIES: > :UNNUMBERED: nil > :END: > *** H3 Fixed. Thanks, Rasmus -- Don't panic!!! ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-21 14:37 ` Rasmus @ 2014-09-21 19:40 ` Nicolas Goaziou 2014-09-21 20:13 ` Rasmus 0 siblings, 1 reply; 27+ messages in thread From: Nicolas Goaziou @ 2014-09-21 19:40 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree, tsd Rasmus <rasmus@gmx.us> writes: > Thanks for the comments. Let's give it another try, shand't we. There we go. > Okay, I returned to my first hack (which never made it to this list). > Basically, I ID everything. Unnumbered sections get the id > "unnumbered-sec-COUNTER" and numbered sections get the id > "sec-COUNTER". > > Perhaps you will find it too much of a hack. I don't think it is a hack. I am just pointing out that how we refer internally to headlines has an effect on output clarity. I let you strike a balance between clarity and easiness of implementation. Note that the internal reference can be a bit cryptic (e.g. num-1-1 and nonum-2). >> At the moment, referring to an unnumbered section displays its name. > > In some modes, yes. In LaTeX it produces a \ref{·} that LaTeX will > laugh at. This is incorrect. #+options: num:nil * Headline :PROPERTIES: :CUSTOM_ID: test :END: This is a link to [[#test]]. will produce \section*{Headline} \label{sec-1} This is a link to \hyperref[sec-1]{Headline}. > If you have a better idea than using the title I'm all ears! On the contrary, using the title is what is usually done. I'm all for it. >> Comparing symbols with `equal' is a sin beyond redemption. Use `eq'. > > Why, out of curiosity? I though equal was like the meaner, tougher > eq, that gets shit right, but is a bit more expensive. This is about using the right tool for the job. Unless you mess with the obarray, two symbols with the same name are guaranteed to be `eq'. There's really no reason to use anything else. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-21 19:40 ` Nicolas Goaziou @ 2014-09-21 20:13 ` Rasmus 2014-09-22 15:53 ` Nicolas Goaziou 0 siblings, 1 reply; 27+ messages in thread From: Rasmus @ 2014-09-21 20:13 UTC (permalink / raw) To: emacs-orgmode Hi Nicolas, Thanks for the comments. Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: >> Okay, I returned to my first hack (which never made it to this list). >> Basically, I ID everything. Unnumbered sections get the id >> "unnumbered-sec-COUNTER" and numbered sections get the id >> "sec-COUNTER". >> >> Perhaps you will find it too much of a hack. > > I don't think it is a hack. I am just pointing out that how we refer > internally to headlines has an effect on output clarity. I let you > strike a balance between clarity and easiness of implementation. Note > that the internal reference can be a bit cryptic (e.g. num-1-1 and > nonum-2). With the last patch it gets weird when you have mixed trees, like this: * numbered ** unnumbered :PROPERTIES: :UNNUMBERED: t :END: The LaTeX output is: \section{numbered} \label{sec-1} \subsection*{unnumbered} \label{unnumbered-sec-0-1} Perhaps it would be nicer to use a single counter rather than two? Right now, this * numbered1 * unnumbered2 :PROPERTIES: :UNNUMBERED: t :END: * numbered2 * unnumbered2 :PROPERTIES: :UNNUMBERED: t :END: produces \section{numbered1} \label{sec-1} \section*{unnumbered2} \label{unnumbered-sec-1} \section{numbered2} \label{sec-2} \section*{unnumbered2} \label{unnumbered-sec-2} But perhaps this is nicer? \label{sec-1} \label{unnumbered-sec-2} \label{sec-3} \label{unnumbered-sec-4} In particular for mixed, nested trees. >>> At the moment, referring to an unnumbered section displays its name. >> >> In some modes, yes. In LaTeX it produces a \ref{·} that LaTeX will >> laugh at. > > This is incorrect. > > #+options: num:nil > > * Headline > :PROPERTIES: > :CUSTOM_ID: test > :END: > This is a link to [[#test]]. > > will produce > > \section*{Headline} > \label{sec-1} > This is a link to \hyperref[sec-1]{Headline}. Is *my statement* incorrect or is the current *output* incorrect? On my PC, when I refer to an unnumbered headline I get \ref{UNNUMBERED}, but since it's after a \section* it will produce nothing or a subsequent element. But I *did* forget to try the patch with emacs -q and maybe that's why I'm not seeing \hyperref's. . . >> If you have a better idea than using the title I'm all ears! > > On the contrary, using the title is what is usually done. I'm all for > it. To be clear: you are happy if it uses the \hyperref[·]{·} in LaTeX, but not \ref{·} for unnumbered? >>> Comparing symbols with `equal' is a sin beyond redemption. Use `eq'. >> >> Why, out of curiosity? I though equal was like the meaner, tougher >> eq, that gets shit right, but is a bit more expensive. > > This is about using the right tool for the job. Unless you mess with the > obarray, two symbols with the same name are guaranteed to be `eq'. > There's really no reason to use anything else. OK. Thanks, Rasmus -- And I faced endless streams of vendor-approved Ikea furniture. . . ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-21 20:13 ` Rasmus @ 2014-09-22 15:53 ` Nicolas Goaziou 2014-09-23 0:35 ` Rasmus 0 siblings, 1 reply; 27+ messages in thread From: Nicolas Goaziou @ 2014-09-22 15:53 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode Hello, Rasmus <rasmus@gmx.us> writes: > With the last patch it gets weird when you have mixed trees, like this: > > * numbered > ** unnumbered > :PROPERTIES: > :UNNUMBERED: t > :END: > > The LaTeX output is: > > \section{numbered} > \label{sec-1} > \subsection*{unnumbered} > \label{unnumbered-sec-0-1} > > Perhaps it would be nicer to use a single counter rather than two? > Right now, this > > * numbered1 > * unnumbered2 > :PROPERTIES: > :UNNUMBERED: t > :END: > * numbered2 > * unnumbered2 > :PROPERTIES: > :UNNUMBERED: t > :END: > > produces > > \section{numbered1} > \label{sec-1} > \section*{unnumbered2} > \label{unnumbered-sec-1} > \section{numbered2} > \label{sec-2} > \section*{unnumbered2} > \label{unnumbered-sec-2} > > But perhaps this is nicer? > > \label{sec-1} > \label{unnumbered-sec-2} > \label{sec-3} > \label{unnumbered-sec-4} > > In particular for mixed, nested trees. I think it would be nice to keep "sec-NUM", with NUM matching current numbering, for numbered headlines. I'm not against a simple global counter for unnumbered headlines: \label{sec-1} \label{unnumbered-1} \label{sec-2} \label{unnumbered-2} or in the following example * H1 ** H2 :PROPERTIES: :UNNUMBERED: t :END: *** H3 *** H4 * H5 ** H6 the labelling scheme \label{sec-1} \label{unnumbered-1} \label{unnumbered-2} \label{unnumbered-3} \label{sec-2} \label{sec-2-1} >> This is incorrect. >> >> #+options: num:nil >> >> * Headline >> :PROPERTIES: >> :CUSTOM_ID: test >> :END: >> This is a link to [[#test]]. >> >> will produce >> >> \section*{Headline} >> \label{sec-1} >> This is a link to \hyperref[sec-1]{Headline}. > > Is *my statement* incorrect or is the current *output* incorrect? The former, but see below. > On my PC, when I refer to an unnumbered headline I get > \ref{UNNUMBERED}, but since it's after a \section* it will produce > nothing or a subsequent element. But I *did* forget to try the patch > with emacs -q and maybe that's why I'm not seeing \hyperref's. . . Actually, there was a small bug in the code, now fixed. `latex' back-end is expected to use "hyperref" when headline in unnumbered. > To be clear: you are happy if it uses the \hyperref[·]{·} in LaTeX, > but not \ref{·} for unnumbered? You are the LaTeX expert. Isn't it reasonable? Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-22 15:53 ` Nicolas Goaziou @ 2014-09-23 0:35 ` Rasmus 2014-09-23 1:10 ` Thomas S. Dye 2014-09-26 7:51 ` Nicolas Goaziou 0 siblings, 2 replies; 27+ messages in thread From: Rasmus @ 2014-09-23 0:35 UTC (permalink / raw) To: emacs-orgmode [-- Attachment #1: Type: text/plain, Size: 2685 bytes --] Hi, Another couple of small changes. Random observation: the patch is "almost" neutral due to the headline-id function: 88 insertions(+), 72 deletions(-) Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: > I think it would be nice to keep "sec-NUM", with NUM matching current > numbering, for numbered headlines. I'm not against a simple global > counter for unnumbered headlines: > > \label{sec-1} > \label{unnumbered-1} > \label{sec-2} > \label{unnumbered-2} > > or in the following example > > * H1 > ** H2 > :PROPERTIES: > :UNNUMBERED: t > :END: > *** H3 > *** H4 > * H5 > ** H6 > > the labelling scheme > > \label{sec-1} > \label{unnumbered-1} > \label{unnumbered-2} > \label{unnumbered-3} > \label{sec-2} > \label{sec-2-1} Using this file: * h1 :PROPERTIES: :CUSTOM_ID: h1 :END: ** h2 :PROPERTIES: :unnumbered: t :CUSTOM_ID: h2 :END: *** h3 *** h4 * h5 :PROPERTIES: :CUSTOM_ID: h5 :END: [[*h1]] [[#h2]] [[*h4]] [[#h5]] ** h6 The output is now \section{h1} \label{sec-1} \subsection*{h2} \label{unnumbered-1} \subsubsection*{h3} \label{unnumbered-2} \subsubsection*{h4} \label{unnumbered-3} \section{h5} \label{sec-2} \ref{sec-1} \hyperref[unnumbered-1]{h2} \hyperref[unnumbered-3]{h4} \ref{sec-2} \subsection{h6} \label{sec-2-1} Which I think is quite good. I don't know if the global unnumbered counter is made in the best way. I add another plist to info with the number. This approach is cleaner than before since it's the numbering of unnumbered headlines is not in `org-export--collect-headline-numbering' which is complicated enough as it is. An alternative approach could be to just "count" the unnumbered headlines place in the tree every time. > Actually, there was a small bug in the code, now fixed. `latex' back-end > is expected to use "hyperref" when headline in unnumbered. Right I see. The attached patch should merge against master. >> To be clear: you are happy if it uses the \hyperref[·]{·} in LaTeX, >> but not \ref{·} for unnumbered? > > You are the LaTeX expert. Isn't it reasonable? It seems to work well, yes. So I guess we should let the patch hang for a while and see if someone cares to test it. Of course I'd appreciate more comments/bug reports. Should I write tests for the new behavior? If so, tests for each backend or only for vanilla-ox functions? Cheers, Rasmus -- If you can mix business and politics wonderful things can happen! [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-ox-Support-unnumbered-headlines-via-property.patch --] [-- Type: text/x-diff, Size: 16271 bytes --] From b6debf17314973b1feb5e7da0d7cc788d4920f50 Mon Sep 17 00:00:00 2001 From: Rasmus <rasmus@gmx.us> Date: Sun, 21 Sep 2014 16:17:55 +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) ox-md.el (org-md-headline, org-md-link), ox-latex.el (org-latex-headline, org.latex-link), ox-html.el (org-html-headline, org-html-link), ox-ascii.el (org-ascii-link): Support ununbered headlines. --- lisp/org.el | 2 +- lisp/ox-ascii.el | 7 +++++-- lisp/ox-html.el | 22 ++++----------------- lisp/ox-latex.el | 20 +++---------------- lisp/ox-md.el | 25 ++++++++++++------------ lisp/ox-odt.el | 25 ++++++++++++------------ lisp/ox.el | 59 +++++++++++++++++++++++++++++++++++++++++++++++--------- 7 files changed, 88 insertions(+), 72 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 4ffe1e8..7565b14 100755 --- a/lisp/org.el +++ b/lisp/org.el @@ -15337,7 +15337,7 @@ but in some other way.") "LOCATION" "LOGGING" "COLUMNS" "VISIBILITY" "TABLE_EXPORT_FORMAT" "TABLE_EXPORT_FILE" "EXPORT_OPTIONS" "EXPORT_TEXT" "EXPORT_FILE_NAME" - "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" + "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" "UNNUMBERED" "ORDERED" "NOBLOCKING" "COOKIE_DATA" "LOG_INTO_DRAWER" "REPEAT_TO_STATE" "CLOCK_MODELINE_TOTAL" "STYLE" "HTML_CONTAINER_CLASS") "Some properties that are used by Org-mode for various purposes. diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el index 6f2b43a..5e8d479 100644 --- a/lisp/ox-ascii.el +++ b/lisp/ox-ascii.el @@ -1530,9 +1530,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 (eq 'headline (org-element-type destination)) + (format "[%s]" (org-export-data (org-element-property :title destination) 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..2dfcd21 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 (and 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) + (and 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 218c69f..8e08213 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)) @@ -1974,14 +1966,8 @@ INFO is a plist holding contextual information. See (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) - "-"))))) + (label (or custom-label + (org-export-get-headline-id destination info)))) (if (and (not desc) (org-export-numbered-headline-p destination info)) (format "\\ref{%s}" label) diff --git a/lisp/ox-md.el b/lisp/ox-md.el index 695fb61..9e7984d 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 + (and (eq '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..372d157 100644 --- a/lisp/ox-odt.el +++ b/lisp/ox-odt.el @@ -1122,7 +1122,7 @@ See `org-odt--build-date-styles' for implementation details." (setq text (concat ;; Section number. - (when section-number (concat section-number ". ")) + (and section-number (concat section-number ". ")) ;; Todo. (when todo (let ((style (if (member todo org-done-keywords) @@ -1816,9 +1816,7 @@ 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))) @@ -1870,9 +1868,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 +2645,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 +2776,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-element-property :title destination))) + (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..5168382 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -1970,6 +1970,7 @@ Return updated plist." ;; properties. (nconc `(:headline-numbering ,(org-export--collect-headline-numbering data info) + :unnumbered-headline-id ,(org-export--collect-unnumbered-headline-id data info) :exported-data ,(make-hash-table :test 'eq :size 4001)) info)) @@ -1992,7 +1993,7 @@ OPTIONS is a plist holding export options." (if (= min-level 10000) 1 min-level)))) (defun org-export--collect-headline-numbering (data options) - "Return numbering of all exportable headlines in a parse tree. + "Return numbering of all exportable numbered headlines in a parse tree. DATA is the parse tree. OPTIONS is the plist holding export options. @@ -2003,7 +2004,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 (not (org-export-numbered-headline-p headline options)) + (org-element-property :footnote-section-p headline)) (let ((relative-level (1- (org-export-get-relative-level headline options)))) (cons @@ -2011,10 +2013,21 @@ for a footnotes section." (loop for n across numbering for idx from 0 to org-export-max-depth when (< idx relative-level) collect n - when (= idx relative-level) collect (aset numbering idx (1+ n)) + when (= idx relative-level) collect (aset numbering + idx (1+ n)) when (> idx relative-level) do (aset numbering idx 0)))))) options))) +(defun org-export--collect-unnumbered-headline-id (data options) + "Return a numbering of all unnumbered headlines. + +Unnumbered headlines are given numbered after occurrence." + (let ((num 0)) + (org-element-map data 'headline + (lambda (headline) + (unless (org-export-numbered-headline-p headline options) + (cons headline (list (setq num (1+ num))))))))) + (defun org-export--populate-ignore-list (data options) "Return list of elements and objects to ignore during export. DATA is the parse tree to traverse. OPTIONS is the plist holding @@ -3772,7 +3785,12 @@ INFO is the plist used as a communication channel." ;; ;; `org-export-get-headline-number' returns the section number of an ;; headline, while `org-export-number-to-roman' allows to convert it -;; to roman numbers. +;; to roman numbers. With an optional argument, +;; `org-export-get-headline-number' returns a number to unnumbered +;; headlines (used for internal id). +;; +;; `org-export-get-headline-id' returns the unique internal id of a +;; headline. ;; ;; `org-export-low-level-p', `org-export-first-sibling-p' and ;; `org-export-last-sibling-p' are three useful predicates when it @@ -3807,17 +3825,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." + (concat + (if (org-export-numbered-headline-p headline info) + "sec-" "unnumbered-") + (mapconcat #'number-to-string + (if (org-export-numbered-headline-p headline info) + (org-export-get-headline-number headline info) + (org-export-get-unnumberd-headline-id headline info)) "-"))) + (defun org-export-get-headline-number (headline info) - "Return HEADLINE numbering as a list of numbers. + "Return numbered HEADLINE numbering as a list of numbers. +INFO is a plist holding contextual information." + (and (org-export-numbered-headline-p headline info) + (cdr (assoc headline (plist-get info :headline-numbering))))) + +(defun org-export-get-unnumberd-headline-id (headline info) + "Return unnumbered HEADLINE id as list of numbers. INFO is a plist holding contextual information." - (cdr (assoc headline (plist-get info :headline-numbering)))) + (and (not (org-export-numbered-headline-p headline info)) + (cdr (assoc headline (plist-get info :unnumbered-headline-id))))) (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 + (or (org-export-get-node-property :UNNUMBERED headline) + (loop for parent in (org-export-get-genealogy headline) + when (org-export-get-node-property :UNNUMBERED parent) + return 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 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-23 0:35 ` Rasmus @ 2014-09-23 1:10 ` Thomas S. Dye 2014-09-26 7:51 ` Nicolas Goaziou 1 sibling, 0 replies; 27+ messages in thread From: Thomas S. Dye @ 2014-09-23 1:10 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode Aloha Rasmus, Rasmus <rasmus@gmx.us> writes: > So I guess we should let the patch hang for a while and see if someone > cares to test it. > > Of course I'd appreciate more comments/bug reports. I'm looking forward to this functionality and will test out the patch now that you're happy with its performance. I have a busy week ahead and two looming deadlines, but I'll get to it as soon as I find time. Thanks for taking on this difficult project. All the best, Tom -- Thomas S. Dye http://www.tsdye.com ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-23 0:35 ` Rasmus 2014-09-23 1:10 ` Thomas S. Dye @ 2014-09-26 7:51 ` Nicolas Goaziou 2014-09-26 13:48 ` Rasmus 2014-09-30 22:54 ` Rasmus 1 sibling, 2 replies; 27+ messages in thread From: Nicolas Goaziou @ 2014-09-26 7:51 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode Hello, Rasmus <rasmus@gmx.us> writes: > Another couple of small changes. Thank you. > Using this file: > > * h1 > :PROPERTIES: > :CUSTOM_ID: h1 > :END: > ** h2 > :PROPERTIES: > :unnumbered: t > :CUSTOM_ID: h2 > :END: > *** h3 > *** h4 > * h5 > :PROPERTIES: > :CUSTOM_ID: h5 > :END: > [[*h1]] [[#h2]] [[*h4]] [[#h5]] > ** h6 > > The output is now > > \section{h1} > \label{sec-1} > \subsection*{h2} > \label{unnumbered-1} > \subsubsection*{h3} > \label{unnumbered-2} > \subsubsection*{h4} > \label{unnumbered-3} > \section{h5} > \label{sec-2} > \ref{sec-1} \hyperref[unnumbered-1]{h2} \hyperref[unnumbered-3]{h4} \ref{sec-2} > \subsection{h6} > \label{sec-2-1} > > Which I think is quite good. I agree. > I don't know if the global unnumbered counter is made in the best way. > I add another plist to info with the number. This approach is cleaner > than before since it's the numbering of unnumbered headlines is not in > `org-export--collect-headline-numbering' which is complicated enough > as it is. 14 locs long functions do not play in the "complicated enough" league. Anyway, your implementation is fine, too. > Should I write tests for the new behavior? If so, tests for each > backend or only for vanilla-ox functions? Tests for "ox.el" are mandatory. See "test-ox.el" > * ox.el (org-export--collect-headline-numbering): Return nil > if unnumbered headline. This is not exactly true: "Ignore unnumbered headlines." would be more appropriate. > (org-export-get-headline-id): New defun that returns a unique > ID to a headline. "New function." is enough. > + (if number > (if (atom number) (number-to-string number) > - (mapconcat 'number-to-string number ".")))))))) > + (mapconcat 'number-to-string number ".")) > + ;; unnumbered headline > + (when (eq 'headline (org-element-type destination)) > + (format "[%s]" (org-export-data (org-element-property :title destination) info))))))))) While you're at it: #'number-to-string. > (ids (delq nil > (list (org-element-property :CUSTOM_ID headline) > - (concat "sec-" section-number) > + (and section-number (concat "sec-" section-number)) > (org-element-property :ID headline)))) > - (preferred-id (car ids)) > + (preferred-id (org-export-get-headline-id headline info)) I think the following is more in the spirit of the code (you don't ignore :custom-id property): (ids (delq nil (list (org-element-property :CUSTOM_ID headline) (org-export-get-headline-id headline info) (org-element-property :ID headline)))) (preferred-id (car ids)) > (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)) This chuck needs to be updated since headline-id doesn't replace :custom-id or :id properties. > (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))) Ditto. > - (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) Ditto. > (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)))))))) No alt title please, as discussed before. (org-export-data (org-element-property :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 > + (and (eq '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)))))))) Ditto. Also, #'number-to-string while you're at it. > (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-element-property :title destination))) (org-export-data (org-element-property :title destination) info) However, I'm not sure this the expected headline-no. > + (unless (or (not (org-export-numbered-headline-p headline options)) > + (org-element-property :footnote-section-p headline)) (when (and (not (org-element-type :footnote-section-p headline)) (org-export-numbered-headline-p headline options)) ...) > +(defun org-export--collect-unnumbered-headline-id (data options) > + "Return a numbering of all unnumbered headlines. > + > +Unnumbered headlines are given numbered after occurrence." You need to give details about the arguments, e.g., "Associate a global counter to all unnumbered headlines DATA is the parse tree. OPTIONS is a plist containing export options." > + (let ((num 0)) > + (org-element-map data 'headline > + (lambda (headline) > + (unless (org-export-numbered-headline-p headline options) > + (cons headline (list (setq num (1+ num))))))))) Last line: (list headline (incf num)) > (defun org-export-get-headline-number (headline info) > - "Return HEADLINE numbering as a list of numbers. > + "Return numbered HEADLINE numbering as a list of numbers. > +INFO is a plist holding contextual information." > + (and (org-export-numbered-headline-p headline info) > + (cdr (assoc headline (plist-get info :headline-numbering))))) Use `assq' instead of `assoc'. > +(defun org-export-get-unnumberd-headline-id (headline info) > + "Return unnumbered HEADLINE id as list of numbers. > INFO is a plist holding contextual information." > - (cdr (assoc headline (plist-get info :headline-numbering)))) > + (and (not (org-export-numbered-headline-p headline info)) > + (cdr (assoc headline (plist-get info :unnumbered-headline-id))))) I don't think it is worth to make this function standalone. I don't see any use case outside `org-export-get-headline-id'. I suggest to move it there. Also, `assoc' -> `assq'. > + (unless > + (or (org-export-get-node-property :UNNUMBERED headline) > + (loop for parent in (org-export-get-genealogy headline) > + when (org-export-get-node-property :UNNUMBERED parent) > + return t)) (unless (org-some (lambda (h) (org-not-nil (org-element-property :UNNUMBERED h))) (org-export-get-genealogy headline)) ...) Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-26 7:51 ` Nicolas Goaziou @ 2014-09-26 13:48 ` Rasmus 2014-09-27 8:19 ` Nicolas Goaziou 2014-09-30 22:54 ` Rasmus 1 sibling, 1 reply; 27+ messages in thread From: Rasmus @ 2014-09-26 13:48 UTC (permalink / raw) To: emacs-orgmode Hi Nicolas, Thanks for all time you've put into the comments. I appreciate it, and I will try to revise the patches over the weekend. Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: >> Using this file: >> >> * h1 >> :PROPERTIES: >> :CUSTOM_ID: h1 >> :END: >> ** h2 >> :PROPERTIES: >> :unnumbered: t >> :CUSTOM_ID: h2 >> :END: >> *** h3 >> *** h4 >> * h5 >> :PROPERTIES: >> :CUSTOM_ID: h5 >> :END: >> [[*h1]] [[#h2]] [[*h4]] [[#h5]] >> ** h6 >> >> The output is now >> >> \section{h1} >> \label{sec-1} >> \subsection*{h2} >> \label{unnumbered-1} >> \subsubsection*{h3} >> \label{unnumbered-2} >> \subsubsection*{h4} >> \label{unnumbered-3} >> \section{h5} >> \label{sec-2} >> \ref{sec-1} \hyperref[unnumbered-1]{h2} >> \hyperref[unnumbered-3]{h4} \ref{sec-2} >> \subsection{h6} >> \label{sec-2-1} >> >> Which I think is quite good. > > I agree. I worry about this approach based on some observations Alan sent off-list. When you export the quoted document with num:nil all labels will be of the form "unnumbered-N", loosing all structure in labels. Also, some labels are still unassigned in html for unnumbered headlines, e.g. the "text-" (which is a function of parents' section numbers) and outline-container-sec-. Do you think it's better to solve the remaining issues, and accept that when num:nil exported documents will be quite altered compared to previously, or should I try to introduce a more informative ID for numbered an unnumbered headlines alike? If following the latter path, the most obvious approach (to me) would be to have a separate :headline-id and :headline-numbering. :headline-id could be collected using something like `org-export--collect-headline-numbering', but labels would not necessarily reflect the printed section numbers, though :headline-numbering would still be "correct". What do you think? —Rasmus -- . . . It begins of course with The Internet. A Net of Peers ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-26 13:48 ` Rasmus @ 2014-09-27 8:19 ` Nicolas Goaziou 0 siblings, 0 replies; 27+ messages in thread From: Nicolas Goaziou @ 2014-09-27 8:19 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode Hello, Rasmus <rasmus@gmx.us> writes: > Thanks for all time you've put into the comments. I appreciate it, > and I will try to revise the patches over the weekend. Thanks. Again, there's no rush. > I worry about this approach based on some observations Alan sent > off-list. When you export the quoted document with num:nil all labels > will be of the form "unnumbered-N", loosing all structure in labels. I don't think how that would be a problem. A label is expected to refer uniquely to a headline. Reflecting structure of the document is not a requisite. "sec-NUM" is but a bonus. As far as labels go, we could also use "headline-X" for all headlines, numbered or not. > Also, some labels are still unassigned in html for unnumbered > headlines, e.g. the "text-" (which is a function of parents' section > numbers) and outline-container-sec-. Couldn't they be turned into "text-unnumbered-X" and "outline-container-unnumbered-X" instead? > Do you think it's better to solve the remaining issues, and accept > that when num:nil exported documents will be quite altered compared to > previously, or should I try to introduce a more informative ID for > numbered an unnumbered headlines alike? The former. > If following the latter path, the most obvious approach (to me) would > be to have a separate :headline-id > and :headline-numbering. :headline-id could be collected using > something like `org-export--collect-headline-numbering', but labels > would not necessarily reflect the printed section numbers, > though :headline-numbering would still be "correct". I think we don't need this added complexity. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-26 7:51 ` Nicolas Goaziou 2014-09-26 13:48 ` Rasmus @ 2014-09-30 22:54 ` Rasmus 2014-10-02 0:35 ` Rasmus 2014-10-03 7:56 ` Nicolas Goaziou 1 sibling, 2 replies; 27+ messages in thread From: Rasmus @ 2014-09-30 22:54 UTC (permalink / raw) To: emacs-orgmode; +Cc: alantyree [-- Attachment #1: Type: text/plain, Size: 3909 bytes --] Hi, Alan did some testing on a slightly older version of this patch and he managed to publish his book without errors and with working links. So let's give it another shot. I briefly tested the output of LaTeX, html, texinfo, odt, md, and plaintext and made sure links work and that the right text is shown in the output. Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: >> Should I write tests for the new behavior? If so, tests for each >> backend or only for vanilla-ox functions? > Tests for "ox.el" are mandatory. See "test-ox.el" I have added some. I don't know if it's comprehensive enough. It was not obvious to me what to test. >> (ids (delq nil >> (list (org-element-property :CUSTOM_ID headline) >> - (concat "sec-" section-number) >> + (and section-number (concat "sec-" section-number)) >> (org-element-property :ID headline)))) >> - (preferred-id (car ids)) >> + (preferred-id (org-export-get-headline-id headline info)) > > I think the following is more in the spirit of the code (you don't > ignore :custom-id property): > > (ids (delq nil > (list (org-element-property :CUSTOM_ID headline) > (org-export-get-headline-id headline info) > (org-element-property :ID headline)))) > (preferred-id (car ids)) But we are not checking that :CUSTOM_ID is unique. In ox-latex you're required to turn on a variable on to get this behavior (I could be mistaken here). For now I have done as you suggest. But I don't understand why we are favoring CUSTOM_ID here over the nice, unique label we've generated? >> - (let ((href .. >> (headline-label ... >> - (org-html--anchor ... I reintroduced the CUSTOM_ID in these. >> + (let ((num 0)) >> + (org-element-map data 'headline >> + (lambda (headline) >> + (unless (org-export-numbered-headline-p headline options) >> + (cons headline (list (setq num (1+ num))))))))) > > Last line: > > (list headline (incf num)) Oh incf is quite handy. Didn't know that one. I leave it as (cons headline (list (incf num))). Why? 'Cause that's the format used by `org-export--collect-headline-numbering'. While simpler is nicer, I think it's better not to have to consider different data structures depending on whether data is from `org-export--collect-headline-numbering' or `org-export--collect-unnumbered-headline-id'. If you feel the simpler structure is better we can also use that. > Use `assq' instead of `assoc'. Right, no need for equal here. >> +(defun org-export-get-unnumberd-headline-id (headline info) >> + "Return unnumbered HEADLINE id as list of numbers. >> INFO is a plist holding contextual information." >> - (cdr (assoc headline (plist-get info :headline-numbering)))) >> + (and (not (org-export-numbered-headline-p headline info)) >> + (cdr (assoc headline (plist-get info :unnumbered-headline-id))))) > > I don't think it is worth to make this function standalone. I don't see > any use case outside `org-export-get-headline-id'. I suggest to move it > there. Yeah, seems fair. >> + (unless >> + (or (org-export-get-node-property :UNNUMBERED headline) >> + (loop for parent in (org-export-get-genealogy headline) >> + when (org-export-get-node-property :UNNUMBERED parent) >> + return t)) > > (unless (org-some > (lambda (h) (org-not-nil (org-element-property :UNNUMBERED h))) > (org-export-get-genealogy headline)) > ...) Handy. AFAIK BLOB is not a member of (org-export-get-genealogy BLOB) (or so the output suggests), so (or · ·) is still needed. Thanks again, Rasmus -- There are known knowns; there are things we know that we know [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-ox-Support-unnumbered-headlines-via-property.patch --] [-- Type: text/x-diff, Size: 20109 bytes --] From e25b297d285b31fd8a842356aa5818d303b4dec9 Mon Sep 17 00:00:00 2001 From: Rasmus <rasmus@gmx.us> Date: Sun, 21 Sep 2014 16:17:55 +0200 Subject: [PATCH] ox: Support unnumbered headlines via property. * ox.el (org-export--collect-headline-numbering): Ignore unnumbered headline. (org-export-get-headline-id, org-export--collect-unnumbered-headline-id): New functions. (org-export-numbered-headline-p): Further tests for unnumbered headline. * ox-odt.el (org-odt-headline, org-odt-link, org-odt-link--infer-description) ox-md.el (org-md-headline, org-md-link), ox-latex.el (org-latex-headline, org.latex-link), ox-html.el (org-html-headline, org-html-link), ox-ascii.el (org-ascii-link): Support ununbered headlines. * test-ox.el (test-org-export/org-export-get-headline-id): New test. --- lisp/org.el | 2 +- lisp/ox-ascii.el | 8 ++++++-- lisp/ox-html.el | 39 +++++++++++++++---------------------- lisp/ox-latex.el | 23 ++++++---------------- lisp/ox-md.el | 24 +++++++++++++---------- lisp/ox-odt.el | 39 +++++++++++++++++-------------------- lisp/ox.el | 52 +++++++++++++++++++++++++++++++++++++++++-------- testing/lisp/test-ox.el | 21 ++++++++++++++++++++ 8 files changed, 126 insertions(+), 82 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 9815eb4..12fabdc 100755 --- a/lisp/org.el +++ b/lisp/org.el @@ -15339,7 +15339,7 @@ but in some other way.") "LOCATION" "LOGGING" "COLUMNS" "VISIBILITY" "TABLE_EXPORT_FORMAT" "TABLE_EXPORT_FILE" "EXPORT_OPTIONS" "EXPORT_TEXT" "EXPORT_FILE_NAME" - "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" + "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" "UNNUMBERED" "ORDERED" "NOBLOCKING" "COOKIE_DATA" "LOG_INTO_DRAWER" "REPEAT_TO_STATE" "CLOCK_MODELINE_TOTAL" "STYLE" "HTML_CONTAINER_CLASS") "Some properties that are used by Org-mode for various purposes. diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el index 6316e59..91bb7a5 100644 --- a/lisp/ox-ascii.el +++ b/lisp/ox-ascii.el @@ -1530,9 +1530,13 @@ 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 (eq 'headline (org-element-type destination)) + (format "[%s]" (org-export-data + (org-element-property :title destination) 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..e32640d 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -2096,8 +2096,7 @@ INFO is a plist used as a communication channel." ;; Label. (org-export-solidify-link-text (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" - (mapconcat #'number-to-string headline-number "-")))) + (org-export-get-headline-id headline info))) ;; Body. (concat (and (not (org-export-low-level-p headline info)) @@ -2321,7 +2320,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 (and 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,7 +2337,7 @@ holding contextual information." (contents (or contents "")) (ids (delq nil (list (org-element-property :CUSTOM_ID headline) - (concat "sec-" section-number) + (org-export-get-headline-id headline info) (org-element-property :ID headline)))) (preferred-id (car ids)) (extra-ids (mapconcat @@ -2369,7 +2368,7 @@ holding contextual information." (org-html--container headline info) (format "outline-container-%s" (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" section-number))) + (org-export-get-headline-id headline info))) (concat (format "outline-%d" level) (and extra-class " ") extra-class) @@ -2808,20 +2807,11 @@ INFO is a plist holding contextual information. See ;; 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")))) + ;; headline linked via CUSTOM_ID + (or (and (string= type "custom-id") + (org-element-property :CUSTOM_ID destination)) + (org-export-get-headline-id destination info) + (t (error "Shouldn't reach here")))) ;; What description to use? (desc ;; Case 1: Headline is numbered and LINK has no @@ -3073,13 +3063,16 @@ holding contextual information." (let* ((class-num (+ (org-export-get-relative-level parent info) (1- (plist-get info :html-toplevel-hlevel)))) (section-number - (mapconcat - 'number-to-string - (org-export-get-headline-number parent info) "-"))) + (and (org-export-numbered-headline-p parent info) + (mapconcat + 'number-to-string + (org-export-get-headline-number parent info) "-")))) ;; Build return value. (format "<div class=\"outline-text-%d\" id=\"text-%s\">\n%s</div>" class-num - (or (org-element-property :CUSTOM_ID parent) section-number) + (or (org-element-property :CUSTOM_ID parent) + section-number + (org-export-get-headline-id parent info)) contents))))) ;;;; Radio Target diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index 5dd79d3..ee48d19 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -1477,15 +1477,10 @@ 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" + (or (and (plist-get info :latex-custom-id-labels) + (org-element-property :CUSTOM_ID headline)) + (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)) @@ -1975,14 +1970,8 @@ INFO is a plist holding contextual information. See (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) - "-"))))) + (label (or custom-label + (org-export-get-headline-id destination info)))) (if (and (not desc) (org-export-numbered-headline-p destination info)) (format "\\ref{%s}" label) diff --git a/lisp/ox-md.el b/lisp/ox-md.el index 695fb61..b1f2bfb 100644 --- a/lisp/ox-md.el +++ b/lisp/ox-md.el @@ -204,10 +204,7 @@ a communication channel." (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-export-get-headline-id headline info)) nil nil info))) ;; Headline text without tags. (heading (concat todo priority title)) @@ -330,10 +327,13 @@ 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-data + (org-element-property :title destination) 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 +354,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 + (and (eq 'headline (org-element-type destination)) + ;; BUG: shouldn't headlines have a form like [ref](name) in md? + (org-export-data (org-element-property :title headline) 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..816bb96 100644 --- a/lisp/ox-odt.el +++ b/lisp/ox-odt.el @@ -1122,7 +1122,7 @@ See `org-odt--build-date-styles' for implementation details." (setq text (concat ;; Section number. - (when section-number (concat section-number ". ")) + (and section-number (concat section-number ". ")) ;; Todo. (when todo (let ((style (if (member todo org-done-keywords) @@ -1789,8 +1789,7 @@ INFO is a plist holding contextual information." (org-element-property :title headline) backend info)) (tags (and (plist-get info :with-tags) (org-export-get-tags headline info))) - (headline-label (concat "sec-" (mapconcat 'number-to-string - headline-number "-"))) + (headline-label (org-export-get-headline-id headline info)) (format-function (if (functionp format-function) format-function (function* @@ -1816,9 +1815,7 @@ 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))) @@ -1870,9 +1867,10 @@ 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 + (if (org-export-numbered-headline-p headline info) "false" "true") (concat extra-targets anchored-title)) contents)))))) @@ -2643,10 +2641,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))))) @@ -2692,11 +2687,15 @@ Return nil, otherwise." item-numbers ""))))) ;; Case 2: Locate a regular and numbered headline in the ;; hierarchy. Display its section number. - (let ((headline (loop for el in (cons destination genealogy) - when (and (eq (org-element-type el) 'headline) - (not (org-export-low-level-p el info)) - (org-export-numbered-headline-p el info)) - return el))) + (let ((headline + (and + ;; Test if destination is a numbered headline + (org-export-numbered-headline-p destination info) + (loop for el in (cons destination genealogy) + when (and (eq (org-element-type el) 'headline) + (not (org-export-low-level-p el info)) + (org-export-numbered-headline-p el info)) + return el)))) ;; We found one. (when headline (format "<text:bookmark-ref text:reference-format=\"chapter\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>" @@ -2776,11 +2775,9 @@ INFO is a plist holding contextual information. See ;; If there's a description, create a hyperlink. ;; 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 "-")))) + (let ((label (or (and (string= type "custom-id") + (org-element-property :CUSTOM_ID destination)) + (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 90c623e..b05e52f 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -1974,6 +1974,7 @@ Return updated plist." ;; properties. (nconc `(:headline-numbering ,(org-export--collect-headline-numbering data info) + :unnumbered-headline-id ,(org-export--collect-unnumbered-headline-id data info) :exported-data ,(make-hash-table :test 'eq :size 4001)) info)) @@ -1996,7 +1997,7 @@ OPTIONS is a plist holding export options." (if (= min-level 10000) 1 min-level)))) (defun org-export--collect-headline-numbering (data options) - "Return numbering of all exportable headlines in a parse tree. + "Return numbering of all exportable, numbered headlines in a parse tree. DATA is the parse tree. OPTIONS is the plist holding export options. @@ -2007,7 +2008,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) + (when (and (org-export-numbered-headline-p headline options) + (not (org-element-property :footnote-section-p headline))) (let ((relative-level (1- (org-export-get-relative-level headline options)))) (cons @@ -2019,6 +2021,19 @@ for a footnotes section." when (> idx relative-level) do (aset numbering idx 0)))))) options))) +(defun org-export--collect-unnumbered-headline-id (data options) + "Return numbering of all exportable, unnumbered headlines in a parse tree. + +DATA is the parse tree. OPTIONS is the plist holding export +options. + +Unnumbered headlines are numbered as a function of occurrence." + (let ((num 0)) + (org-element-map data 'headline + (lambda (headline) + (unless (org-export-numbered-headline-p headline options) + (cons headline (list (incf num)))))))) + (defun org-export--populate-ignore-list (data options) "Return list of elements and objects to ignore during export. DATA is the parse tree to traverse. OPTIONS is the plist holding @@ -3776,7 +3791,12 @@ INFO is the plist used as a communication channel." ;; ;; `org-export-get-headline-number' returns the section number of an ;; headline, while `org-export-number-to-roman' allows to convert it -;; to roman numbers. +;; to roman numbers. With an optional argument, +;; `org-export-get-headline-number' returns a number to unnumbered +;; headlines (used for internal id). +;; +;; `org-export-get-headline-id' returns the unique internal id of a +;; headline. ;; ;; `org-export-low-level-p', `org-export-first-sibling-p' and ;; `org-export-last-sibling-p' are three useful predicates when it @@ -3811,17 +3831,33 @@ 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." + (let ((numbered (org-export-numbered-headline-p headline info))) + (concat + (if numbered "sec-" "unnumbered-") + (mapconcat #'number-to-string + (if numbered + (org-export-get-headline-number headline info) + (cdr (assq headline (plist-get info :unnumbered-headline-id)))) "-")))) + (defun org-export-get-headline-number (headline info) - "Return HEADLINE numbering as a list of numbers. + "Return numbered HEADLINE numbering as a list of numbers. INFO is a plist holding contextual information." - (cdr (assoc headline (plist-get info :headline-numbering)))) + (and (org-export-numbered-headline-p headline info) + (cdr (assq headline (plist-get info :headline-numbering))))) (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 + (or (org-element-property :UNNUMBERED headline) + (org-some (lambda (head) (org-not-nil (org-element-property :UNNUMBERED head))) + (org-export-get-genealogy headline))) + (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." diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index 4af3510..8e5e5d3 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -1576,6 +1576,27 @@ Paragraph[fn:1]" (lambda (h) (org-export-numbered-headline-p h info)) (plist-put info :section-numbers t))))) +(ert-deftest test-org-export/org-export-get-headline-id () + "Test `org-export-get-headline-id' specifications." + (should + (equal "sec-1" + (org-test-with-parsed-data "* Headline" + (org-export-get-headline-id + (org-element-map tree 'headline 'identity info t) + info)))) + (should + (equal "unnumbered-1" + (org-test-with-parsed-data "* Headline\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" + (org-export-get-headline-id + (org-element-map tree 'headline 'identity info t) + info)))) + (should + (equal "unnumbered-1" + (org-test-with-parsed-data "* Headline\n#+OPTIONS: num:nil" + (org-export-get-headline-id + (org-element-map tree 'headline 'identity info t) + info))))) + (ert-deftest test-org-export/number-to-roman () "Test `org-export-number-to-roman' specifications." ;; If number is negative, return it as a string. -- 2.1.1 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-30 22:54 ` Rasmus @ 2014-10-02 0:35 ` Rasmus 2014-10-03 7:56 ` Nicolas Goaziou 1 sibling, 0 replies; 27+ messages in thread From: Rasmus @ 2014-10-02 0:35 UTC (permalink / raw) To: emacs-orgmode; +Cc: alantyree ["Cloud-note" to self (probably) or, preferably, someone else] One "deficit" of unnumbered headlines in odt is that they are indented. This looks dumb! Here's some notes on how I think is the best way to get rid of the indention in ox-odt. If this bothers someone, please go ahead and fix it! Otherwise I might look at it eventually. . . Alan L Tyree <alantyree@gmail.com> writes: > Also interesting [to note on unnumbered headlines in LO]: right > click on the unnumbered headline. Go to Bullets and Numbering. Click > on 'Remove' at the bottom and the headline jumps back into proper > alignment. Arh, so indention for unnumbered headlines can be removed! Studying the xml files of LO it seems the mechanism is something like this. Basically, to get an unnumbered headline you have to specify it as a list header. This is what the current patch does and how LO does it. But the indention is caused by the fact that it's a list header. To get rid of the indention one has to specify the list style to be the empty style "" (empty quote). . . So, for unnumbered headlines, append text:is-list-header="true" to the text:h specification. Example: <text:h text:style-name="Heading_20_1" text:outline-level=1 text:is-list-header="true">headline</text:h> To get it unindented we need to add style:list-style-name="" To the style. LO does this by generating PN, N is an int, styles on the fly that are derived from Heading styles: <style:style style:name="P2" style:family="paragraph" style:parent-style-name="Heading_20_1" style:list-style-name=""/> In Org, I think the easiest thing to do would be to add these styles to the styles.xml file and choose the right style based on whether headline is numbered or not numbered. . . Unfortunately, it seems we cannot just add style:list-style-name="" to the end of the style definition of Heading_20_N as this kills the number. —Rasmus -- Dobbelt-A ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-09-30 22:54 ` Rasmus 2014-10-02 0:35 ` Rasmus @ 2014-10-03 7:56 ` Nicolas Goaziou 2014-10-03 8:49 ` Sebastien Vauban 2014-10-03 10:26 ` Rasmus 1 sibling, 2 replies; 27+ messages in thread From: Nicolas Goaziou @ 2014-10-03 7:56 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree Hello, Rasmus <rasmus@gmx.us> writes: > Alan did some testing on a slightly older version of this patch and he > managed to publish his book without errors and with working links. So > let's give it another shot. > > I briefly tested the output of LaTeX, html, texinfo, odt, md, and > plaintext and made sure links work and that the right text is shown in > the output. Thanks. It looks mostly good. Some minor comments follow. >> I think the following is more in the spirit of the code (you don't >> ignore :custom-id property): >> >> (ids (delq nil >> (list (org-element-property :CUSTOM_ID headline) >> (org-export-get-headline-id headline info) >> (org-element-property :ID headline)))) >> (preferred-id (car ids)) > > But we are not checking that :CUSTOM_ID is unique. This is not our problem, but user's. > In ox-latex you're required to turn on a variable on to get this > behavior (I could be mistaken here). For now I have done as you > suggest. But I don't understand why we are favoring CUSTOM_ID here > over the nice, unique label we've generated? We could do the same as ox-latex, default to generated label, and optionally allow users to use raw custom-id instead (with usual caveat). Meanwhile, I think it is reasonable to stick to the current behaviour. >> Last line: >> >> (list headline (incf num)) > > Oh incf is quite handy. Didn't know that one. > > I leave it as (cons headline (list (incf num))). Why? 'Cause that's > the format used by `org-export--collect-headline-numbering'. While > simpler is nicer, I think it's better not to have to consider > different data structures depending on whether data is from > `org-export--collect-headline-numbering' or > `org-export--collect-unnumbered-headline-id'. I don't get your point. (cons 'a (list 'b)) is equivalent to (list 'a 'b). Why do you think this changes the data structure? >> (unless (org-some >> (lambda (h) (org-not-nil (org-element-property :UNNUMBERED h))) >> (org-export-get-genealogy headline)) >> ...) > > Handy. AFAIK BLOB is not a member of (org-export-get-genealogy BLOB) > (or so the output suggests), so (or · ·) is still needed. I think (org-some (lambda (h) ...) (cons headline (org-export-get-genealogy headline))) is more elegant. > + ;; headline linked via CUSTOM_ID ;; Headline linked via CUSTOM_ID. > + (or (and (string= type "custom-id") > + (org-element-property :CUSTOM_ID destination)) > + (org-export-get-headline-id destination info) > + (t (error "Shouldn't reach here")))) > ;; What description to use? > (desc > ;; Case 1: Headline is numbered and LINK has no > @@ -3073,13 +3063,16 @@ holding contextual information." > (let* ((class-num (+ (org-export-get-relative-level parent info) > (1- (plist-get info :html-toplevel-hlevel)))) > (section-number > - (mapconcat > - 'number-to-string > - (org-export-get-headline-number parent info) "-"))) > + (and (org-export-numbered-headline-p parent info) > + (mapconcat > + 'number-to-string Nitpick: #'number-to-string > + ;; Test if destination is a numbered headline Missing full stop. > + (let ((num 0)) > + (org-element-map data 'headline > + (lambda (headline) > + (unless (org-export-numbered-headline-p headline options) > + (cons headline (list (incf num)))))))) See above. > + (unless > + (or (org-element-property :UNNUMBERED headline) > + (org-some (lambda (head) (org-not-nil (org-element-property :UNNUMBERED head))) > + (org-export-get-genealogy headline))) > + (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)))) Per above (unless (org-some (lambda (h) (org-not-nil (org-element-property :UNNUMBERED h))) (cons headline (org-export-get-genealogy headline))) ...) > +(ert-deftest test-org-export/org-export-get-headline-id () > + "Test `org-export-get-headline-id' specifications." > + (should > + (equal "sec-1" > + (org-test-with-parsed-data "* Headline" > + (org-export-get-headline-id > + (org-element-map tree 'headline 'identity info t) > + info)))) > + (should > + (equal "unnumbered-1" > + (org-test-with-parsed-data "* Headline\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" > + (org-export-get-headline-id > + (org-element-map tree 'headline 'identity info t) > + info)))) > + (should > + (equal "unnumbered-1" > + (org-test-with-parsed-data "* Headline\n#+OPTIONS: num:nil" > + (org-export-get-headline-id > + (org-element-map tree 'headline 'identity info t) > + info))))) I suggest to also test tricky inherited UNNUMBERED properties * H :PROPERTIES: :UNNUMBERED: t :END: ** H2 :PROPERTIES: :UNNUMBERED: nil :END: *** H3 Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-10-03 7:56 ` Nicolas Goaziou @ 2014-10-03 8:49 ` Sebastien Vauban 2014-10-03 10:26 ` Rasmus 1 sibling, 0 replies; 27+ messages in thread From: Sebastien Vauban @ 2014-10-03 8:49 UTC (permalink / raw) To: emacs-orgmode-mXXj517/zsQ Nicolas Goaziou wrote: >> + ;; Test if destination is a numbered headline > > Missing full stop. Small question about conventions for writing comments... Can I consider the following to be the rule? - If it's a *sentence* (like the above example, in the imperative form), begin with a capital and end with a dot. - Otherwise (like ";; pattern found"), begin with a small letter and don't end with a dot. ? Tips on writing comments found on https://www.gnu.org/software/emacs/manual/html_node/elisp/Comment-Tips.html aren't very helpful IMHO, as you see things such as: - "; there was there was a base version to which this looks like a subversion", that is a sentence with no capital nor dot... - ";; Update mode line.", that is a sentence with both beginning capital and to. - ";;;; The kill ring", an expression with a capital... That page seems to be more on the usage of ";", ";;", ";;;" and ";;;;" than on the usage of capitals and dots. Best regards, Seb -- Sebastien Vauban ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-10-03 7:56 ` Nicolas Goaziou 2014-10-03 8:49 ` Sebastien Vauban @ 2014-10-03 10:26 ` Rasmus 2014-10-03 20:14 ` Nicolas Goaziou 1 sibling, 1 reply; 27+ messages in thread From: Rasmus @ 2014-10-03 10:26 UTC (permalink / raw) To: mail, emacs-orgmode; +Cc: alantyree [-- Attachment #1: Type: text/plain, Size: 1211 bytes --] Hi, Now odt output doesn't look ugly. I fixed the other things you mentioned. Make test passes. Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: >> But we are not checking that :CUSTOM_ID is unique. > > This is not our problem, but user's. > >> In ox-latex you're required to turn on a variable on to get this >> behavior (I could be mistaken here). For now I have done as you >> suggest. But I don't understand why we are favoring CUSTOM_ID here >> over the nice, unique label we've generated? > > We could do the same as ox-latex, default to generated label, and > optionally allow users to use raw custom-id instead (with usual caveat). > > Meanwhile, I think it is reasonable to stick to the current behaviour. Fine with me. Maybe some other day. . . > I don't get your point. (cons 'a (list 'b)) is equivalent to (list 'a > 'b). Why do you think this changes the data structure? Right. No point. > (org-some (lambda (h) ...) > (cons headline (org-export-get-genealogy headline))) > > is more elegant. That's true. > I suggest to also test tricky inherited UNNUMBERED properties Added. —Rasmus -- Vote for proprietary math! [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-ox-Support-unnumbered-headlines-via-property.patch --] [-- Type: text/x-diff, Size: 28689 bytes --] From 75309757d30c9920077d1ec82a61713830cb4888 Mon Sep 17 00:00:00 2001 From: Rasmus <rasmus@gmx.us> Date: Sun, 21 Sep 2014 16:17:55 +0200 Subject: [PATCH] ox: Support unnumbered headlines via property. * ox.el (org-export--collect-headline-numbering): Ignore unnumbered headline. (org-export-get-headline-id, org-export--collect-unnumbered-headline-id): New functions. (org-export-numbered-headline-p): Further tests for unnumbered headline. * ox-odt.el (org-odt-headline, org-odt-link, org-odt-link--infer-description) ox-md.el (org-md-headline, org-md-link), ox-latex.el (org-latex-headline, org.latex-link), ox-html.el (org-html-headline, org-html-link), ox-ascii.el (org-ascii-link): Support ununbered headlines. * test-ox.el (test-org-export/org-export-get-headline-id): New test. * OrgOdtStyles.xml: Add styles for unnumbered headings. --- etc/styles/OrgOdtStyles.xml | 19 ++++++++++++++ lisp/org.el | 2 +- lisp/ox-ascii.el | 8 ++++-- lisp/ox-html.el | 40 +++++++++++++---------------- lisp/ox-latex.el | 23 +++++------------ lisp/ox-md.el | 24 ++++++++++-------- lisp/ox-odt.el | 46 ++++++++++++++++----------------- lisp/ox.el | 52 +++++++++++++++++++++++++++++++------ testing/lisp/test-ox.el | 62 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 191 insertions(+), 85 deletions(-) diff --git a/etc/styles/OrgOdtStyles.xml b/etc/styles/OrgOdtStyles.xml index f41d984..a164335 100644 --- a/etc/styles/OrgOdtStyles.xml +++ b/etc/styles/OrgOdtStyles.xml @@ -109,34 +109,53 @@ </style:style> <style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="1" style:class="text"> <style:text-properties fo:font-size="115%" fo:font-weight="bold" style:font-size-asian="115%" style:font-weight-asian="bold" style:font-size-complex="115%" style:font-weight-complex="bold"/> + <style:style style:name="Heading_20_1_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_1" style:list-style-name=""> </style:style> <style:style style:name="Heading_20_2" style:display-name="Heading 2" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="2" style:class="text"> <style:text-properties fo:font-size="14pt" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="14pt" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-style-complex="italic" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_2_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_2" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_3" style:display-name="Heading 3" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="3" style:class="text"> <style:text-properties fo:font-size="14pt" fo:font-weight="bold" style:font-size-asian="14pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_3_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_3" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_4" style:display-name="Heading 4" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="4" style:class="text"> <style:text-properties fo:font-size="85%" fo:font-style="italic" fo:font-weight="bold" style:font-size-asian="85%" style:font-style-asian="italic" style:font-weight-asian="bold" style:font-size-complex="85%" style:font-style-complex="italic" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_4_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_4" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_5" style:display-name="Heading 5" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="5" style:class="text"> <style:text-properties fo:font-size="85%" fo:font-weight="bold" style:font-size-asian="85%" style:font-weight-asian="bold" style:font-size-complex="85%" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_5_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_5" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_6" style:display-name="Heading 6" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="6" style:class="text"> <style:text-properties fo:font-size="75%" fo:font-weight="bold" style:font-size-asian="75%" style:font-weight-asian="bold" style:font-size-complex="75%" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_6_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_6" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_7" style:display-name="Heading 7" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="7" style:class="text"> <style:text-properties fo:font-size="75%" fo:font-weight="bold" style:font-size-asian="75%" style:font-weight-asian="bold" style:font-size-complex="75%" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_7_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_7" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_8" style:display-name="Heading 8" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="8" style:class="text"> <style:text-properties fo:font-size="75%" fo:font-weight="bold" style:font-size-asian="75%" style:font-weight-asian="bold" style:font-size-complex="75%" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_8_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_8" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_9" style:display-name="Heading 9" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="9" style:class="text"> <style:text-properties fo:font-size="75%" fo:font-weight="bold" style:font-size-asian="75%" style:font-weight-asian="bold" style:font-size-complex="75%" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_9_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_9" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_10" style:display-name="Heading 10" style:family="paragraph" style:parent-style-name="Heading" style:next-style-name="Text_20_body" style:default-outline-level="10" style:class="text"> <style:text-properties fo:font-size="75%" fo:font-weight="bold" style:font-size-asian="75%" style:font-weight-asian="bold" style:font-size-complex="75%" style:font-weight-complex="bold"/> </style:style> + <style:style style:name="Heading_20_10_unnumbered" style:family="paragraph" style:parent-style-name="Heading_20_10" style:list-style-name=""> + </style:style> <style:style style:name="Heading_20_1.title" style:display-name="Heading 1.title" style:family="paragraph" style:parent-style-name="Heading_20_1"> <style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/> </style:style> diff --git a/lisp/org.el b/lisp/org.el index efe7cc5..a6d8d1b 100755 --- a/lisp/org.el +++ b/lisp/org.el @@ -15340,7 +15340,7 @@ but in some other way.") "LOCATION" "LOGGING" "COLUMNS" "VISIBILITY" "TABLE_EXPORT_FORMAT" "TABLE_EXPORT_FILE" "EXPORT_OPTIONS" "EXPORT_TEXT" "EXPORT_FILE_NAME" - "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" + "EXPORT_TITLE" "EXPORT_AUTHOR" "EXPORT_DATE" "UNNUMBERED" "ORDERED" "NOBLOCKING" "COOKIE_DATA" "LOG_INTO_DRAWER" "REPEAT_TO_STATE" "CLOCK_MODELINE_TOTAL" "STYLE" "HTML_CONTAINER_CLASS") "Some properties that are used by Org-mode for various purposes. diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el index 6316e59..959de8e 100644 --- a/lisp/ox-ascii.el +++ b/lisp/ox-ascii.el @@ -1530,9 +1530,13 @@ 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 (eq 'headline (org-element-type destination)) + (format "[%s]" (org-export-data + (org-element-property :title destination) 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..7656163 100644 --- a/lisp/ox-html.el +++ b/lisp/ox-html.el @@ -2096,8 +2096,7 @@ INFO is a plist used as a communication channel." ;; Label. (org-export-solidify-link-text (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" - (mapconcat #'number-to-string headline-number "-")))) + (org-export-get-headline-id headline info))) ;; Body. (concat (and (not (org-export-low-level-p headline info)) @@ -2321,7 +2320,8 @@ 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 (and 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,7 +2338,7 @@ holding contextual information." (contents (or contents "")) (ids (delq nil (list (org-element-property :CUSTOM_ID headline) - (concat "sec-" section-number) + (org-export-get-headline-id headline info) (org-element-property :ID headline)))) (preferred-id (car ids)) (extra-ids (mapconcat @@ -2369,7 +2369,7 @@ holding contextual information." (org-html--container headline info) (format "outline-container-%s" (or (org-element-property :CUSTOM_ID headline) - (concat "sec-" section-number))) + (org-export-get-headline-id headline info))) (concat (format "outline-%d" level) (and extra-class " ") extra-class) @@ -2808,20 +2808,11 @@ INFO is a plist holding contextual information. See ;; 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")))) + ;; Headline linked via CUSTOM_ID. + (or (and (string= type "custom-id") + (org-element-property :CUSTOM_ID destination)) + (org-export-get-headline-id destination info) + (t (error "Shouldn't reach here")))) ;; What description to use? (desc ;; Case 1: Headline is numbered and LINK has no @@ -3073,13 +3064,16 @@ holding contextual information." (let* ((class-num (+ (org-export-get-relative-level parent info) (1- (plist-get info :html-toplevel-hlevel)))) (section-number - (mapconcat - 'number-to-string - (org-export-get-headline-number parent info) "-"))) + (and (org-export-numbered-headline-p parent info) + (mapconcat + #'number-to-string + (org-export-get-headline-number parent info) "-")))) ;; Build return value. (format "<div class=\"outline-text-%d\" id=\"text-%s\">\n%s</div>" class-num - (or (org-element-property :CUSTOM_ID parent) section-number) + (or (org-element-property :CUSTOM_ID parent) + section-number + (org-export-get-headline-id parent info)) contents))))) ;;;; Radio Target diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el index 5dd79d3..ee48d19 100644 --- a/lisp/ox-latex.el +++ b/lisp/ox-latex.el @@ -1477,15 +1477,10 @@ 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" + (or (and (plist-get info :latex-custom-id-labels) + (org-element-property :CUSTOM_ID headline)) + (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)) @@ -1975,14 +1970,8 @@ INFO is a plist holding contextual information. See (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) - "-"))))) + (label (or custom-label + (org-export-get-headline-id destination info)))) (if (and (not desc) (org-export-numbered-headline-p destination info)) (format "\\ref{%s}" label) diff --git a/lisp/ox-md.el b/lisp/ox-md.el index 695fb61..67b30af 100644 --- a/lisp/ox-md.el +++ b/lisp/ox-md.el @@ -204,10 +204,7 @@ a communication channel." (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-export-get-headline-id headline info)) nil nil info))) ;; Headline text without tags. (heading (concat todo priority title)) @@ -330,10 +327,13 @@ 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-data + (org-element-property :title destination) 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 +354,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. + (and (eq 'headline (org-element-type destination)) + ;; BUG: shouldn't headlines have a form like [ref](name) in md? + (org-export-data (org-element-property :title headline) 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..b5d6d48 100644 --- a/lisp/ox-odt.el +++ b/lisp/ox-odt.el @@ -1122,7 +1122,7 @@ See `org-odt--build-date-styles' for implementation details." (setq text (concat ;; Section number. - (when section-number (concat section-number ". ")) + (and section-number (concat section-number ". ")) ;; Todo. (when todo (let ((style (if (member todo org-done-keywords) @@ -1789,8 +1789,7 @@ INFO is a plist holding contextual information." (org-element-property :title headline) backend info)) (tags (and (plist-get info :with-tags) (org-export-get-tags headline info))) - (headline-label (concat "sec-" (mapconcat 'number-to-string - headline-number "-"))) + (headline-label (org-export-get-headline-id headline info)) (format-function (if (functionp format-function) format-function (function* @@ -1815,10 +1814,9 @@ holding contextual information." (full-text (org-odt-format-headline--wrap headline nil info)) ;; Get level relative to current parsed data. (level (org-export-get-relative-level headline info)) + (numbered (org-export-numbered-headline-p 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))) @@ -1842,8 +1840,7 @@ holding contextual information." (and (org-export-first-sibling-p headline info) (format "\n<text:list text:style-name=\"%s\" %s>" ;; Choose style based on list type. - (if (org-export-numbered-headline-p headline info) - "OrgNumberedList" "OrgBulletedList") + (if numbered "OrgNumberedList" "OrgBulletedList") ;; If top-level list, re-start numbering. Otherwise, ;; continue numbering. (format "text:continue-numbering=\"%s\"" @@ -1870,9 +1867,11 @@ holding contextual information." (t (concat (format - "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\">%s</text:h>" - (format "Heading_20_%s" level) + "\n<text:h text:style-name=\"%s\" text:outline-level=\"%s\" text:is-list-header=\"%s\">%s</text:h>" + (format "Heading_20_%s%s" + level (if numbered "" "_unnumbered")) level + (if numbered "false" "true") (concat extra-targets anchored-title)) contents)))))) @@ -2643,10 +2642,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))))) @@ -2692,11 +2688,15 @@ Return nil, otherwise." item-numbers ""))))) ;; Case 2: Locate a regular and numbered headline in the ;; hierarchy. Display its section number. - (let ((headline (loop for el in (cons destination genealogy) - when (and (eq (org-element-type el) 'headline) - (not (org-export-low-level-p el info)) - (org-export-numbered-headline-p el info)) - return el))) + (let ((headline + (and + ;; Test if destination is a numbered headline. + (org-export-numbered-headline-p destination info) + (loop for el in (cons destination genealogy) + when (and (eq (org-element-type el) 'headline) + (not (org-export-low-level-p el info)) + (org-export-numbered-headline-p el info)) + return el)))) ;; We found one. (when headline (format "<text:bookmark-ref text:reference-format=\"chapter\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>" @@ -2776,11 +2776,9 @@ INFO is a plist holding contextual information. See ;; If there's a description, create a hyperlink. ;; 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 "-")))) + (let ((label (or (and (string= type "custom-id") + (org-element-property :CUSTOM_ID destination)) + (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 52d3535..53ebf36 100644 --- a/lisp/ox.el +++ b/lisp/ox.el @@ -1974,6 +1974,7 @@ Return updated plist." ;; properties. (nconc `(:headline-numbering ,(org-export--collect-headline-numbering data info) + :unnumbered-headline-id ,(org-export--collect-unnumbered-headline-id data info) :exported-data ,(make-hash-table :test 'eq :size 4001)) info)) @@ -1996,7 +1997,7 @@ OPTIONS is a plist holding export options." (if (= min-level 10000) 1 min-level)))) (defun org-export--collect-headline-numbering (data options) - "Return numbering of all exportable headlines in a parse tree. + "Return numbering of all exportable, numbered headlines in a parse tree. DATA is the parse tree. OPTIONS is the plist holding export options. @@ -2007,7 +2008,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) + (when (and (org-export-numbered-headline-p headline options) + (not (org-element-property :footnote-section-p headline))) (let ((relative-level (1- (org-export-get-relative-level headline options)))) (cons @@ -2019,6 +2021,19 @@ for a footnotes section." when (> idx relative-level) do (aset numbering idx 0)))))) options))) +(defun org-export--collect-unnumbered-headline-id (data options) + "Return numbering of all exportable, unnumbered headlines in a parse tree. + +DATA is the parse tree. OPTIONS is the plist holding export +options. + +Unnumbered headlines are numbered as a function of occurrence." + (let ((num 0)) + (org-element-map data 'headline + (lambda (headline) + (unless (org-export-numbered-headline-p headline options) + (list headline (incf num))))))) + (defun org-export--populate-ignore-list (data options) "Return list of elements and objects to ignore during export. DATA is the parse tree to traverse. OPTIONS is the plist holding @@ -3873,7 +3888,12 @@ INFO is the plist used as a communication channel." ;; ;; `org-export-get-headline-number' returns the section number of an ;; headline, while `org-export-number-to-roman' allows to convert it -;; to roman numbers. +;; to roman numbers. With an optional argument, +;; `org-export-get-headline-number' returns a number to unnumbered +;; headlines (used for internal id). +;; +;; `org-export-get-headline-id' returns the unique internal id of a +;; headline. ;; ;; `org-export-low-level-p', `org-export-first-sibling-p' and ;; `org-export-last-sibling-p' are three useful predicates when it @@ -3908,17 +3928,33 @@ 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." + (let ((numbered (org-export-numbered-headline-p headline info))) + (concat + (if numbered "sec-" "unnumbered-") + (mapconcat #'number-to-string + (if numbered + (org-export-get-headline-number headline info) + (cdr (assq headline (plist-get info :unnumbered-headline-id)))) "-")))) + (defun org-export-get-headline-number (headline info) - "Return HEADLINE numbering as a list of numbers. + "Return numbered HEADLINE numbering as a list of numbers. INFO is a plist holding contextual information." - (cdr (assoc headline (plist-get info :headline-numbering)))) + (and (org-export-numbered-headline-p headline info) + (cdr (assq headline (plist-get info :headline-numbering))))) (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-some + (lambda (head) (org-not-nil (org-element-property :UNNUMBERED head))) + (cons headline (org-export-get-genealogy headline))) + (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." diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el index 2642f4a..10561b0 100644 --- a/testing/lisp/test-ox.el +++ b/testing/lisp/test-ox.el @@ -1633,6 +1633,68 @@ Paragraph[fn:1]" (lambda (h) (org-export-numbered-headline-p h info)) (plist-put info :section-numbers t))))) +(ert-deftest test-org-export/org-export-get-headline-id () + "Test `org-export-get-headline-id' specifications." + ;; Numbered headlines have IDs akin to "sec-N". + (should + (equal "sec-1" + (org-test-with-parsed-data "* H" + (org-export-get-headline-id + (org-element-map tree 'headline 'identity info t) + info)))) + ;; The ID of numbered headlines reflect the hierarchy. + (should + (equal "sec-1-1" + (org-test-with-parsed-data "* H1\n** H2" + (org-export-get-headline-id + (org-element-map tree 'headline + (lambda (h) + (and (equal "H2" (org-element-property :raw-value h)) h)) + info t) + info)))) + ;; Unnumbered headlines have IDs akin to "unnumbered-N". + (should + (equal "unnumbered-1" + (org-test-with-parsed-data + "* H\n:PROPERTIES:\n:UNNUMBERED: t\n:END:" + (org-export-get-headline-id + (org-element-map tree 'headline 'identity info t) + info)))) + ;; The ID of Unnumbered headlines do not reflect the hierarchy. + (should + (equal "unnumbered-2" + (org-test-with-parsed-data + "* H1\n:PROPERTIES:\n:UNNUMBERED: t\n:END:\n** H2" + (org-export-get-headline-id + (org-element-map tree 'headline + (lambda (h) (and + (equal "H2" (org-element-property :raw-value h)) h)) + info t) info)))) + ;; When #+OPTIONS: num:nil all headlines are unnumbered. + (should + (equal "unnumbered-1" + (org-test-with-parsed-data "* H\n#+OPTIONS: num:nil" + (org-export-get-headline-id + (org-element-map tree 'headline 'identity info t) + info)))) + ;; UNNUMBERED is inherited from parents. + (should + (org-test-with-parsed-data + "* H +:PROPERTIES: +:UNNUMBERED: t +:END: +** H2 +:PROPERTIES: +:UNNUMBERED: nil +:END: +*** H3" + (org-every + (lambda (headline) (not (org-export-numbered-headline-p headline info))) + (org-element-map tree 'headline + 'identity + info))))) + (ert-deftest test-org-export/number-to-roman () "Test `org-export-number-to-roman' specifications." ;; If number is negative, return it as a string. -- 2.1.2 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-10-03 10:26 ` Rasmus @ 2014-10-03 20:14 ` Nicolas Goaziou 2014-10-03 20:31 ` Rasmus 0 siblings, 1 reply; 27+ messages in thread From: Nicolas Goaziou @ 2014-10-03 20:14 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree Hello, Rasmus <rasmus@gmx.us> writes: > Now odt output doesn't look ugly. I fixed the other things you > mentioned. Make test passes. Applied. Thank you very much. Now, some documentation would be nice. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-10-03 20:14 ` Nicolas Goaziou @ 2014-10-03 20:31 ` Rasmus 2014-10-05 8:06 ` Nicolas Goaziou 0 siblings, 1 reply; 27+ messages in thread From: Rasmus @ 2014-10-03 20:31 UTC (permalink / raw) To: emacs-orgmode; +Cc: alantyree Nicolas Goaziou <mail@nicolasgoaziou.fr> writes: > Hello, > > Rasmus <rasmus@gmx.us> writes: > >> Now odt output doesn't look ugly. I fixed the other things you >> mentioned. Make test passes. > > Applied. Thank you very much. Thanks. Bastien added my key to org-git today, so hopefully you'll be spared of this in the future. > Now, some documentation would be nice. Yeah. I wans't sure where to put it. There's 7. Properties and Columns: Storing information about an entry But this doesn't seem export specific. E.g. CUSTOMID is not in the sec. 7.2 table. Then there's 12. Exporting, where I guess the closest fitting section is 12.3 Export settings. Any opinions? —Rasmus -- Lasciate ogni speranza o voi che entrate: siete nella mani di'machellaio ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch, ox] Unnumbered headlines 2014-10-03 20:31 ` Rasmus @ 2014-10-05 8:06 ` Nicolas Goaziou 0 siblings, 0 replies; 27+ messages in thread From: Nicolas Goaziou @ 2014-10-05 8:06 UTC (permalink / raw) To: Rasmus; +Cc: emacs-orgmode, alantyree Hello, Rasmus <rasmus@gmx.us> writes: > Then there's 12. Exporting, where I guess the closest fitting section > is 12.3 Export settings. > > Any opinions? I think section 12.3 is fine, as an additional paragraph within the num: entry. Regards, -- Nicolas Goaziou ^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2014-10-05 8:05 UTC | newest] Thread overview: 27+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-08-08 13:39 [patch, ox] Unnumbered headlines Rasmus 2014-08-08 22:35 ` Alan L Tyree 2014-08-09 1:04 ` [patch, ox] Unnumbered headlines - early test Alan L Tyree 2014-08-09 7:47 ` [patch, ox] Unnumbered headlines Detlef Steuer 2014-08-11 14:18 ` Nicolas Goaziou 2014-08-11 15:37 ` Rasmus 2014-08-12 8:58 ` Nicolas Goaziou 2014-09-20 16:02 ` Rasmus 2014-09-20 20:34 ` Alan L Tyree 2014-09-21 13:12 ` Nicolas Goaziou 2014-09-21 14:37 ` Rasmus 2014-09-21 19:40 ` Nicolas Goaziou 2014-09-21 20:13 ` Rasmus 2014-09-22 15:53 ` Nicolas Goaziou 2014-09-23 0:35 ` Rasmus 2014-09-23 1:10 ` Thomas S. Dye 2014-09-26 7:51 ` Nicolas Goaziou 2014-09-26 13:48 ` Rasmus 2014-09-27 8:19 ` Nicolas Goaziou 2014-09-30 22:54 ` Rasmus 2014-10-02 0:35 ` Rasmus 2014-10-03 7:56 ` Nicolas Goaziou 2014-10-03 8:49 ` Sebastien Vauban 2014-10-03 10:26 ` Rasmus 2014-10-03 20:14 ` Nicolas Goaziou 2014-10-03 20:31 ` Rasmus 2014-10-05 8:06 ` Nicolas Goaziou
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).