As requested the problem can't be resolved (with input similar to the example). TeX is sequentially working and when it executes \printendedsections
in running text there is no way for TeX to know if the next thing that comes up is more text, some heading or ...
Even with a multi-pass solution it is next to impossible to determine, at least not if you do not additionally modify the heading commands as well, as one would need to determine what (if anything) is between \printendedsections
and the next heading command.
Ignoring, for the moment, the additional request to determine if a section starts on the top of the page, the problem of automatically outputting text when a section level ends can be accomplished in a number of ways. Here is one that does everything automagically:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\patchcmd\@startsection%
{ \par}{\par \outputsectionends{#2}}%
{\typeout{*** SUCCESS ***}}{\typeout{*** FAIL ***}}
\def\perhapssomethingended #1{%
\ifnum \arabic{#1}>\z@
(end of #1 \csname the#1\endcsname)%
\fi
}
\makeatother
\def\outputsectionends #1{%
\ifcase #1\relax
% extend here (+ \chapter command) for report/book class
\or
\perhapssomethingended{subsubsection}
\perhapssomethingended{subsection}
\perhapssomethingended{section}
\setcounter{subsubsection}{0} % need to reset high-level headings as LaTeX is lazy and only resets the next level
\or
\perhapssomethingended{subsubsection}
\perhapssomethingended{subsection}
\or
\perhapssomethingended{subsubsection}
\fi
\par
}
\AtEndDocument{\outputsectionends1}
\begin{document}
\section{sec1} asdf
\subsection{sub1.1} asdf
\subsection{sub1.2} asdf
\subsubsection{sub1.2.1} asdf
\paragraph{sub1.2.1.1} this level is not taken into account (but could of course)
\paragraph{sub1.2.1.2} this level is not taken into account
\section{sec2} asdf
\subsection{sub2.1}
\subsection{sub2.2} asdf
\end{document}
Explanation:
We hook into \@startsection
which is the standard LaTeX interface for producing headings (other than chapter). We know that if this command is called a heading with "level number" #2
is being generated (a section is 1, a subsection 2 ...).
So we do different things depending on the number: if we start a section (1) then we may have ended a "subsubsection" and perhaps a "subsection" and perhaps a previous "section". So we run \perhapssomethingended
for each. This command looks at the value of the current counter (given as its argument), say for "subsection" and if this value is greater then zero, then there has been a previous heading. Thus we output some text.
There is only one catch: LaTeX is lazy and when it starts a new heading it only resets the next heading level to zero but not all higher heading levels (as this will later on recursively happen anyway). However, for us this is a problem because it means that after "subsubsection 1.2.1" its value will stay at 1, thus messing up the output when "subsection 2.1" is processed. Therefore we explicitly have to ensure that the subsubsection counter is reset when we process a section (you can try it out by commenting the \setcounter
line out).
Finally we also need to end everything at the end of the document. This is done by pretending that this place acts like another section, i.e., we run \outputsectionends{1}
there.
If you run this you will get:
Of course there are several improvements possible including, e.g., how the resulting text is formatted (which for now is simply as a paragraph with indentation by itself).
Detecting sections on the top of the page
Obviously this doesn't work with \afterpage
as it all happens behind the scene. However afterpage isn't the right tool anyway here, as it drops in something in an asyncronous way after the main galley has already been processed and this isn't going to help.
The approach that I would use instead is adding labels before and after the breakpoint that is added by the heading. Thus if both end up on the same page, then no break was taken at this point and if they end up on different pages, then the break happened directly in front of the heading. In that case \outputsectionends
could change its behavior as necessary (obviously that would result in multiple runs before the document stabilizes, especially if the amount of text generated changes a lot depending on the situation). I'm not too sure though what is wanted by the OP so I'm not going to flesh this out.
Providing this solution for parts or chapter
In a comment it was asked if this solution would work also \part
or \chapter
. The answer is yes and no. Not out of the box with the code above. It needs extending the code in two ways
\chapter
and \part
are normally not defined using \@startsection
. Thus their code would need to be patch in a similar way, so that before they generate the heading (and possibly a page break etc) the \outputsectionends
is run.
- In addition
\outputsectionends
would need to be changed: the hierarchy level of \chapter
is 0 and that of \part
is -1. So the \ifcase
could be extended to accomodate chapter (there is already a comment in the code where to place the actions), but for \part
one needs to additionally test for #1
being -1 if \ifcase
only runs through 0, 1, 2, ...
Update
In a comment it was asked what I mean by adding labels before and after the break. What \@startsection
uses to generate the possible breakpoint is:
\addpenalty\@secpenalty\addvspace\@tempskipa
and the commands \addpenalty
and \addvspace
are a little tricky so that we can't simply add labels before and after. Why? Because they look backwards to detect if there is a vertical space in front and if so move the space after the penalty or combine it with the additionally requested space (in case of \addvspace
). So if we put a label in front of \addpenalty
it means that any previous space is hidden by it. Therefore we need to provide our own version that mimics \addpenalty
and also adds a label:
\def\addpenaltywithlabel#1#2{%
\ifvmode
\if@minipage
\else
\if@nobreak
\else
\ifdim\lastskip=\z@
\label{#2}% <- one place for the label
\penalty#1\relax
\else
\@tempskipb\lastskip
\vskip -\lastskip
\label{#2}% <- and here if the previous skip is "moved"
\penalty#1%
\vskip\@tempskipb
\fi
\fi
\fi
\else
\@noitemerr
\fi}
Adding the second label after the \addvspace
is fine as that will be followed by the heading title. There is of course a lot more to do, the labels need to be different for each instance (for example by using a counter that is incremented) and one has to write a comparison for the page values they generate. And then one has to write some code that actually makes use of this information that the heading appears right at the top (hand waving :-)
\printendedsections
as well? Maybe I'm misunderstanding what you really wish to achieve.\printendedsections
is a contrived example to illustrate the general idea. I'm interested in a general solution mostly because I'm curious and I think it will come in handy in the future, but also because I have a specific problem I'd like to solve.