Hi Ihor, I’m on the road and with no access to the development computer right now.
Answers inline: > El 23 feb 2025, a las 10:24, Ihor Radchenko <yanta...@posteo.net> escribió: > > Pedro Andres Aranda Gutierrez <paag...@gmail.com> writes: > >>> May you please explain this change? >> >> At one point, while debugging, it helped me assuring need-alternative being >> a boolean. Ignore if you don’t like it. >>>> - ;; legacy behavior (á la LaTeX) >>>> + ;; Treat the ToC as it has been originally >>> >>> Maybe "Ignore unnumbered headings in ToC - as in LaTeX" >> >> OK, could live with that. > > I have updated your patch, addressing my comments, adding some > refactoring, and fixing a bug I found. > See the attached updated patch. > > Main changes: > > 1. > > (when (and (not (string= full-text-no-footnote full-text)) ;; when we have > footnotess > (string= full-text opt-title)) ;; And we do not > impose an alternative title > (setq opt-title full-text-no-footnote)) > > You previously had (string= full-text-no-footenote opt-title), which > makes no sense. I also added a test for the case addressed by this part > of the code. > > 2. I renamed need-alternative to need-toc. IMHO, it is a bit more > clear. I also updated the commentary to explain what is going on. I was thinking more in the line of need-alternative-p since all this comes when we guess we need an alternative text in the ToC > 3. I added a FIXME > > ;; FIXME: In theory, user may customize section-fmt > ;; to use, e.g. \section{...} for unnumbered headings > ;; We do not handle such scenario. > > Not sure if it is something we need to worry about. Nice to have, Would need to to through all template packages to see if they use something different. Better document we aren’t 1000 per mil sure... > In addition, I have a question about > > (if (string-suffix-p "*" section-kw) > ;; Subsection that needs alternative title: > ;; Keep section format, use \\addcontentsline > (setq new-extra > (format "\\addcontentsline{toc}{%s}{%s}\n" > (string-remove-suffix "*" section-kw) > opt-title)) > ;; section... we need the brackets > (let* > ;; Replace square brackets with parenthesis > ;; since square brackets are not supported in > ;; optional arguments. > ((un-bracketed-alt (replace-regexp-in-string > "\\[" "(" > (replace-regexp-in-string "\\]" ")" opt-title))) > (replacement-re (concat "\\1[" un-bracketed-alt "]"))) > (setq new-format (replace-match replacement-re nil nil > section-fmt 1)))) > > I am wondering if we can sompletely drop the part with using > \sectionkwd[...]{...} > and instead always use \addcontentsline. This way, we won't need to > replace square brackets in the title. WDYT? No please… It would make no sense from a LaTeX point-of-view and someone with LaTeX background make well think there is a bug… Let’s keep LaTeX output LaTeX ;-) Re the patch: if the tests pass, go ahead with it and let’s move forward. Best, /PA > > From 278f4dd91919c505e108f3d0b4714c363c6be55c Mon Sep 17 00:00:00 2001 > Message-ID: > <278f4dd91919c505e108f3d0b4714c363c6be55c.1740302355.git.yanta...@posteo.net> > From: "Pedro A. Aranda" <paag...@gmail.com> > Date: Wed, 12 Feb 2025 07:27:27 +0100 > Subject: [PATCH v2] New combined patch for ToC handling > > Ihor's comments and fix alternative title handling > --- > doc/org-manual.org | 41 +++++++++++- > etc/ORG-NEWS | 15 +++++ > lisp/ox-latex.el | 114 ++++++++++++++++++++++++++------ > testing/lisp/test-ox-latex.el | 118 ++++++++++++++++++++++++++++++++++ > 4 files changed, 266 insertions(+), 22 deletions(-) > > diff --git a/doc/org-manual.org b/doc/org-manual.org > index 65a1185d17..dcc9a6dec0 100644 > --- a/doc/org-manual.org > +++ b/doc/org-manual.org > @@ -14852,6 +14852,41 @@ *** Quote blocks in LaTeX export > \end{foreigndisplayquote} > #+end_example > > +*** Controlling the way the [[*Table of Contents]] is generated > +#+cindex: LaTeX ToC export > + > +When exporting your document to LaTeX, only numbered sections will be > +included. This is closer to LaTeX and different to how other exporters > +work. If you need an unnumbered section to appear in the table of > +contents, use the property =UNNUMBERED= and set it to =toc=: > + > +#+begin_example > +:PROPERTIES: > +:UNNUMBERED: toc > +:END: > +#+end_example > + > +#+cindex: LaTeX ToC export a la ~org~ > +#+cindex: @samp{org-latex-toc-include-unnumbered} > +If you want the LaTeX exporter to behave like other exporters, > +customise the variable ~org-latex-toc-include-unnumbered~ and > +set it to ~t~: > + > +#+begin_example > +(setq org-latex-toc-include-unnumbered t) > +#+end_example > + > +or add this setting in the local variables. > + > +In this case, unnumbered sections will be included in the table of > +contents, unless you set the =UNNUMBERED= property to ~notoc~: > + > +#+begin_example > +:PROPERTIES: > +:UNNUMBERED: notoc > +:END: > +#+end_example > + > ** Markdown Export > :PROPERTIES: > :DESCRIPTION: Exporting to Markdown. > @@ -23509,8 +23544,10 @@ * Footnotes > configuration. See [[LaTeX specific export settings]]. > > [fn:43] At the moment, some export backends do not obey this > -specification. For example, LaTeX export excludes every unnumbered > -headline from the table of contents. > +specification. For example, LaTeX export excludes by default > +every unnumbered headline from the table of contents, unless you set > +the custom variable =org-latex-toc-include-unnumbered= to =t= or add > +=:UNNUMBERED: toc= to the section's properties. > > [fn:44] Note that ~org-link-search-must-match-exact-headline~ is > locally bound to non-~nil~. Therefore, ~org-link-search~ only matches > diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS > index 9eb4f711c1..7f067481e2 100644 > --- a/etc/ORG-NEWS > +++ b/etc/ORG-NEWS > @@ -305,6 +305,21 @@ slide to specific animation steps. > This text will be displayed on animation step 2 and later. > #+END_SRC > > +*** ox-latex: Table of contents generation has been fixed and augmented > + > +The LaTeX exporter differs from other exporters in that it *does not* > +export unnumbered sections by default. > + > +The LaTeX exporter will use the new property value =:UNNUMBERED: toc= to > +include unnumbered sections in the table of contents. > + > +Additionally, a new custom variable > +~org-latex-toc-include-unnumbered~ has been introduced. It > +enables including unnumbered sections in the ToC aligned with the > +behaviour of other exporters. In this case, to exclude a section from > +the table of contents, mark it as =:UNNUMBERED: notoc= in its > +properties. > + > ** New functions and changes in function arguments > > # This also includes changes in function behavior from Elisp perspective. > diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el > index bc31df6f52..dbfcdacf9c 100644 > --- a/lisp/ox-latex.el > +++ b/lisp/ox-latex.el > @@ -643,7 +643,8 @@ (defcustom org-latex-hyperref-template > :version "26.1" > :package-version '(Org . "8.3") > :type '(choice (const :tag "No template" nil) > - (string :tag "Format string"))) > + (string :tag "Format string")) > + :safe #'string-or-null-p) > > ;;;; Headline > > @@ -675,6 +676,7 @@ (defcustom org-latex-default-footnote-command > "\\footnote{%s%s}" > The value will be passed as an argument to `format' as the following > (format org-latex-default-footnote-command > footnote-description footnote-label)" > + > :group 'org-export-latex > :package-version '(Org . "9.7") > :type 'string) > @@ -1537,6 +1539,18 @@ (defcustom org-latex-known-warnings > (string :tag "Message")))) > > > +(defcustom org-latex-toc-include-unnumbered nil > + "Set this variable to true to include unnumbered headings in the > +table of contents as other exporters do. > + > +The default behaviour is to include numbered headings only. > +To include an unnumbered heading, set the `:UNNUMBERED:' > +property to `toc'" > + :group 'org-export-latex > + :package-version '(Org . "9.8") > + :type 'boolean > + :safe #'booleanp) > + > > ;;; Internal Functions > > @@ -2275,6 +2289,12 @@ (defun org-latex-headline (headline contents info) > (unless (org-element-property :footnote-section-p headline) > (let* ((class (plist-get info :latex-class)) > (level (org-export-get-relative-level headline info)) > + ;; "LaTeX TOC handling" > + ;; :unnumbered: toc will add the heading to the ToC > + ;; "Org TOC handling" > + ;; :unnumbered: notoc to suppress heading from the ToC > + ;; else include all headings (including unnumbered) like other > modes > + (unnumbered-type (org-export-get-node-property :UNNUMBERED > headline t)) > (numberedp (org-export-numbered-headline-p headline info)) > (class-sectioning (assoc class (plist-get info :latex-classes))) > ;; Section formatting will set two placeholders: one for > @@ -2381,6 +2401,8 @@ (defun org-latex-headline (headline contents info) > (funcall (plist-get info :latex-format-headline-function) > todo todo-type priority > (org-export-data-with-backend > + ;; Returns alternative title when provided or > + ;; title itself. > (org-export-get-alt-title headline info) > section-backend info) > (and (eq (plist-get info :with-tags) t) tags) > @@ -2401,25 +2423,77 @@ (defun org-latex-headline (headline contents info) > (string-match-p "\\<local\\>" v) > (format "\\stopcontents[level-%d]" level))))) > info t))))) > - (if (and (or (and opt-title (not (equal opt-title full-text))) > - ;; Heading contains footnotes. Add optional title > - ;; version without footnotes to avoid footnotes in > - ;; TOC/footers. > - (and (not (equal full-text-no-footnote full-text)) > - (setq opt-title full-text-no-footnote))) > - (string-match "\\`\\\\\\(.+?\\){" section-fmt)) > - (format (replace-match "\\1[%s]" nil nil section-fmt 1) > - ;; Replace square brackets with parenthesis > - ;; since square brackets are not supported in > - ;; optional arguments. > - (replace-regexp-in-string > - "\\[" "(" (replace-regexp-in-string "\\]" ")" opt-title)) > - full-text > - (concat headline-label pre-blanks contents)) > - ;; Impossible to add an alternative heading. Fallback to > - ;; regular sectioning format string. > - (format section-fmt full-text > - (concat headline-label pre-blanks contents)))))))) > + ;; When do we need to explicitly specify a heading for TOC? > + ;; 1. On numbered section with footnotes in title or alt_title > + ;; 2. On an unnumbered section if :UNNUMBERED: allows it > regardless of footnotes > + ;; This applies to anything that may go into the ToC. > + ;; Specifically for paragraphs, see first answer of > + ;; > https://tex.stackexchange.com/questions/288072/footnotes-within-paragraph > + (let ((section-kw > + (and (string-match "\\`\\\\\\(.+?\\){" section-fmt) > + (match-string 1 section-fmt))) > + need-toc) > + (if (not section-kw) > + ;; We only know how to add \SECTION-KW{...} to TOC. > + (setq need-toc nil) > + (if (string-suffix-p "*" section-kw) > + ;; FIXME: In theory, user may customize section-fmt > + ;; to use, e.g. \section{...} for unnumbered headings > + ;; We do not handle such scenario. > + (progn ;; unnumbered sections (ending with *) > + ;; Then we need to obey what the :UNNUMBERED: property > says > + (if org-latex-toc-include-unnumbered > + ;; Treat the ToC closer to what other exporters do > + ;; Include unnumbered section into TOC unless > + ;; explicitly requested not to. > + (if (string= unnumbered-type "notoc") > + (setq need-toc nil) > + (setq need-toc t)) > + ;; Ignore unnumbered headings in ToC - as in LaTeX > + ;; unless explicitly requested to include. > + (if (string= unnumbered-type "toc") > + (setq need-toc t) > + (setq need-toc nil)))) > + ;; Numbered sections > + ;; Specify special TOC title only when there is > + ;; opt-title or when title contains footnotes. > + (if (and (string= full-text full-text-no-footnote) ;; no > footnotes > + ;; opt-title is either ALT_TITLE or title itself > + ;; as returned by `org-export-get-alt-title' > + (string= full-text opt-title)) ;; same alternative > title > + (setq need-toc nil) > + (setq need-toc t)))) > + ;; In all cases > + ;; Get rid of the footnotes in opt-title > + (when (and (not (string= full-text-no-footnote full-text)) ;; > when we have footnotess > + (string= full-text opt-title)) ;; And we do not > impose an alternative title > + (setq opt-title full-text-no-footnote)) > + (if need-toc > + (let ((new-format section-fmt) > + (new-extra "")) ;; put the addcontentsline here > + (if (string-suffix-p "*" section-kw) > + ;; Subsection that needs alternative title: > + ;; Keep section format, use \\addcontentsline > + (setq new-extra > + (format "\\addcontentsline{toc}{%s}{%s}\n" > + (string-remove-suffix "*" section-kw) > + opt-title)) > + ;; section... we need the brackets > + (let* > + ;; Replace square brackets with parenthesis > + ;; since square brackets are not supported in > + ;; optional arguments. > + ((un-bracketed-alt (replace-regexp-in-string > + "\\[" "(" > (replace-regexp-in-string "\\]" ")" opt-title))) > + (replacement-re (concat "\\1[" un-bracketed-alt > "]"))) > + (setq new-format (replace-match replacement-re nil nil > section-fmt 1)))) > + (format new-format > + full-text > + (concat headline-label new-extra pre-blanks > contents))) > + ;; Don't need or cannot have alternative heading. > + ;; Use regular sectioning format string. > + (format section-fmt full-text > + (concat headline-label pre-blanks contents))))))))) > > (defun org-latex-format-headline-default-function > (todo _todo-type priority text tags _info) > diff --git a/testing/lisp/test-ox-latex.el b/testing/lisp/test-ox-latex.el > index b75921ae78..04164767c0 100644 > --- a/testing/lisp/test-ox-latex.el > +++ b/testing/lisp/test-ox-latex.el > @@ -154,5 +154,123 @@ (ert-deftest test-ox-latex/inline-image () > (search-forward > > "\\href{https://orgmode.org/worg/images/orgmode/org-mode-unicorn.svg}{\\includegraphics[width=.9\\linewidth]{/wallpaper.png}}")))) > > +(ert-deftest test-ox-latex/num-t () > + "Test toc treatment for fixed num:t" > + (org-test-with-exported-text > + 'latex > + "#+TITLE: num: fix > +#+OPTIONS: toc:t H:3 num:t > + > +* Section > + > +** Subsection 1 > +:PROPERTIES: > +:UNNUMBERED: t > +:END: > +is suppressed > +** Subsection 2 > +:PROPERTIES: > +:UNNUMBERED: toc > +:END: > + > +** Subsection 3 > +:PROPERTIES: > +:UNNUMBERED: toc > +:ALT_TITLE: Alternative > +:END: > + > +* Section 2[fn::Test] > +:PROPERTIES: > +:ALT_TITLE: SECTION 2 > +:END: > +" > + (goto-char (point-min)) > + (should > + (search-forward "\\begin{document} > + > +\\maketitle > +\\tableofcontents > + > +\\section{Section} > +\\label{")) > + (should (search-forward "} > + > +\\subsection*{Subsection 1} > +\\label{")) > + (should (search-forward "} > +is suppressed > +\\subsection*{Subsection 2} > +\\label{")) > + (should (search-forward "} > +\\addcontentsline{toc}{subsection}{Subsection 2} > +\\subsection*{Subsection 3} > +\\label{")) > + (should (search-forward "} > +\\addcontentsline{toc}{subsection}{Alternative} > +\\section[SECTION 2]{Section 2\\footnote{Test}} > +\\label{")) > + (should (search-forward "} > +\\end{document}")))) > + > +(ert-deftest test-ox-latex/new-toc-as-org () > + "test toc treatment with `org-latex-toc-include-unnumbered' set to `t'" > + (let ((org-latex-toc-include-unnumbered t)) > + (org-test-with-exported-text 'latex > + "#+TITLE: num: fix > +#+OPTIONS: toc:t H:3 num:nil > + > +* Section > + > +** Subsection 1 > + > +** Subsection 2 > +:PROPERTIES: > +:UNNUMBERED: notoc > +:END: > +is suppressed > + > +** Subsection 3 > +:PROPERTIES: > +:ALT_TITLE: Alternative > +:END: > + > +* Section 2[fn::Test] > +:PROPERTIES: > +:ALT_TITLE: SECTION 2 > +:END: > + > +* Section 3[fn::Test] > +" > + (goto-char (point-min)) > + (should (search-forward "\\begin{document} > + > +\\maketitle > +\\tableofcontents > + > +\\section*{Section} > +\\label{")) > + (should (search-forward "} > +\\addcontentsline{toc}{section}{Section} > + > +\\subsection*{Subsection 1} > +\\label{")) > + (should (search-forward "} > +\\addcontentsline{toc}{subsection}{Subsection 1} > + > +\\subsection*{Subsection 2} > +\\label{")) > + (should (search-forward "} > +is suppressed > +\\subsection*{Subsection 3} > +\\label{")) > + (should (search-forward "} > +\\addcontentsline{toc}{subsection}{Alternative} > +\\section*{Section 2\\footnote{Test}} > +\\label{")) > + (should (search-forward "} > +\\addcontentsline{toc}{section}{SECTION 2}")) > + (should (search-forward "} > +\\addcontentsline{toc}{section}{Section 3}"))))) > + > (provide 'test-ox-latex) > ;;; test-ox-latex.el ends here > -- > 2.47.1 > > > -- > Ihor Radchenko // yantar92, > Org mode maintainer, > Learn more about Org mode at <https://orgmode.org/>. > Support Org development at <https://liberapay.com/org-mode>, > or support my work at <https://liberapay.com/yantar92>