16

In Is it a bad idea to use \begin{section} ... \end{section} in LaTeX?, Jan Hlavacek says:

Of course it is typically possible to recognize an end of a section, but there is actually a number of ways how a section can end: start of another section, start of the next chapter or part, start of \endmatter, bibliograpy, index, etc, or the end of document.

How would I go about writing a macro that recognizes when it is at the end of a section?

I'd like it to work properly in \afterpage so that I can fix some of the issues in my answer to Section reminders at the top of each page? I believe this means that it'll have to use marks to detect where it is in relation to a sectioning command, but I don't know enough about LaTeX to be certain.

For example, I'd like to be able to do something like the following:

\documentclass{article}
\usepackage{afterpage}
\newcommand{\printendedsections}{%
    \ifatendofsubsubsection{(end of subsubsection \thesubsubsection)}{}%
    \ifatendofsubsection{(end of subsection \thesubsection)}{}%
    \ifatendofsection{(end of section \thesection)}{}%
    \ifatendofchapter{(end of chapter \thechapter)}{}%
    \ifatendofpart{(end of part \thepart)}{}%
}
\begin{document}

% will expand to '(end of subsection 2.1)' at the top of page 2 because
% subsection 2.2 starts right at the top of page 2
\afterpage{\hbox{\printendedsections}}

\section{sec1}

asdf
\printendedsections% expands to nothing because section 1 hasn't ended yet

\subsection{sub1.1}

\printendedsections% expands to nothing because subsection 1.1 hasn't ended yet
asdf
\printendedsections% expands to '(end of subsection 1.1)(end of section 1)'

\section{sec2}

asdf
\printendedsections% expands to nothing because section 2 hasn't ended yet

\subsection{sub2.1}

pretend there is enough content here
to push the start of subsection 2.2
to the top of the next page
\printendedsections% expands to '(end of subsection 2.1)'

\subsection{sub2.2}

asdf
\printendedsections% expands to '(end of subsection 2.2)(end of section 2)'

\end{document}
6
  • Related: tex.stackexchange.com/q/50783/5763 Commented Apr 10, 2012 at 19:16
  • 1
    Most of the negative comments in the referred question are related to the grouping introduced by the environment. It would be easy to define macros for "sectioning environments" which don't introduce groups but are still able to check for proper nesting, for example. And you'd have an explicit place to bind the section ending stuff to. Any other solution to your request would be really awkward. Commented Apr 10, 2012 at 20:44
  • @StephanLehmke: Requiring users to declare the end of a section would make this much easier, but it would mean that I couldn't write a package that others could just drop into their documents and use. Commented Apr 10, 2012 at 22:08
  • @RichardHansen wouldn't this hold for \printendedsections as well? Maybe I'm misunderstanding what you really wish to achieve. Commented Apr 10, 2012 at 22:12
  • @StephanLehmke: \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. Commented Apr 10, 2012 at 22:47

1 Answer 1

21

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:

enter image description here

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

  1. \chapterand \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.
  2. 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 :-)

12
  • Mittelback Did not try it out, but off the top of my head will fail for starred versions. Better to add a flag or boolean to keep track of sections. Set it at one after the command. Test before it, if one end of section etc.. Pretty much like one would keep track of brackets in a compiler.
    – yannisl
    Commented Apr 30, 2012 at 20:53
  • @Yiannis the name is Middle spring or creek if you and to anglizise me ;-) but back to the question: it can't fail on star commands as there is no spec on what they should produce when ending ... their title? This would bring us to quite another ball game Commented Apr 30, 2012 at 21:00
  • Oops! Sorry about the name my bad. The reason I brought the starred version of the command, is that a lot of books end sections with ornaments. This sort of books very rarely have sections numbered.
    – yannisl
    Commented Apr 30, 2012 at 21:07
  • @Yiannis no problem (about the name - you aren't the first one ... most often it is Mittleback or a slight variation). Anyway, yes I understand that. My solution is not adequate to monitor or trap the heading structure including starred ones, but that wasn't asked for, was it? Commented Apr 30, 2012 at 21:15
  • 1
    @Louis I added some explanation to to extend this code for \part and \chapter. But I assume that what are looking for is simpler: doubt that you are interested in which sections and subsections have ended, but just in the fact that a part has ended. In that case I would be lazy and just put such a slide in before each part, or define a macro that does it for me. Commented Aug 10, 2012 at 7:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .