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