Table of Contents – Unable to Add to ‘After’ Hook of Sectioning Command in LaTeX

hookslthookssectioningtable of contentstocloft

It seems one cannot add to the "after" hook of a sectioning command. For example the document

\documentclass{article}
\AddToHook{cmd/section/after}{\bfseries}
\begin{document}
abc
\section{some title}
abc
\end{document}

fails with the log reading

! Missing \endcsname inserted.
<to be read again> 
                   \scan_stop: 
l.5 \section
            {hhd}
The control sequence marked <to be read again> should
not appear between \csname and \endcsname.

! Argument of \hook_use:n has an extra }.
<inserted text> 
                \par 
l.5 \section
            {hhd}
I've run across a `}' that doesn't seem to match anything.
For example, `\def\a#1{...}' and `\a}' would produce
this error. If you simply proceed now, the `\par' that
I've just inserted will cause me to report a runaway
argument that might be the root of the problem. But if
your `}' was spurious, just type `2' and it will go away.

Runaway argument?
! Paragraph ended before \hook_use:n was complete.
<to be read again> 
                   \par 
l.5 \section
            {hhd}
I suspect you've forgotten a `}', causing me to apply this
control sequence to too much text. How can we recover?
My plan is to forget the whole thing and hope for the best.

The error is the same if \section is replaced with \chapter, \subsection, etc. Of course this example does not show the use; my actual use case is to add a Section <num> line to a custom toc made with \newlistof from tocloft. A simple workaround by hooking before the command and adding one to the counter is shown in the example.

\documentclass{article}
\usepackage{tocloft,lipsum}

\newlistof[section]{answer}{ans}{List of Answers}

\newcommand{\answer}[1]{%
    \refstepcounter{answer}
    \par\noindent\textbf{Answer \theanswer. #1}
    \addcontentsline{ans}{answer}{\protect\numberline{\theanswer}#1}\par
    }

%%% this doesn't work
% \AddToHook{cmd/section/after}{
%   \addcontentsline{ans}{section}{Section \thesection}
%   }

%%% workaround
\newcounter{sectionplusone}
\setcounter{sectionplusone}{1}
\AddToHook{cmd/section/before}{
    \addcontentsline{ans}{section}{Section \thesectionplusone}
    \stepcounter{sectionplusone}
    }

\begin{document}
\section{some section}
\answer{Title} \lipsum[1][1-2]
\section{another section}
\answer{another title} \lipsum[1][3-4]
\listofanswer
\end{document}

enter image description here

Is this supposed to error? If so, what is the suggested way to patch "after" a sectioning command without redefining it?

Best Answer

This is documented in the section 2. Restrictions and Operational details, and particularly the example of cmd/section/after is given:

enter image description here

This boils down to how each command is implemented, and as the docs say, either you know or you find out (the hard way :). About \section, in particular, there is a step-by-step explanation of how it works here, which might help you understand why this fails.

This cannot be detected by the hook code, and it fails in rather unpredictable ways. How it fails also depends on how the command is defined.

If you are using LaTeX with standard classes and no package for formatting sectioning commands, you can add the hook to \@xsect, which is run after every sectioning command, but here you no longer know if it was a \section or \subsection or some other. Another option is to hook into \sectionmark, \subsectionmark, etc: they are rather “user level” and not problematic to hook into:

\documentclass{article}
\usepackage{tocloft,lipsum}

\newlistof[section]{answer}{ans}{List of Answers}

\newcommand{\answer}[1]{%
    \refstepcounter{answer}
    \par\noindent\textbf{Answer \theanswer. #1}
    \addcontentsline{ans}{answer}{\protect\numberline{\theanswer}#1}\par
    }

\AddToHook{cmd/sectionmark/after}
  {\addcontentsline{ans}{section}{Section \thesection}}

\begin{document}
\section{some section}
\answer{Title} \lipsum[1][1-2]
\section{another section}
\answer{another title} \lipsum[1][3-4]
\listofanswer
\end{document}