[Tex/LaTex] Missing Item number in enumerate

boxesconditionals

You would think that I would know better than to mess with an egreg solution, but in my defense I used a DavidCarlisle solution from \setbox vs. \sbox and \savebox – What are the differences I need to know about? to adapt egreg's solution to Proper way to detect empty/blank text.
This has worked fine for me up until now, but when I attempt to use it within an enumerate environment it results in it gobbling up the item number:

enter image description here

Note that the item number is not gobbled for the case where there is no title to be used (Third Item).

If I replace the \sbox0{\begingroup\vbox{#1}\endgroup} as per the original solution:

\sbox0{#1}

this particular MWE work, but causes problems elsewhere (not shown here).

Question:

So, what is the minimal change I could make to the existing \sbox0 solution so that the item numbers is not gobbledr?

I am disabling some macros within the \begingroup so as to eliminate any side effects so do need the grouping.

Code:

\documentclass{article}
\usepackage{enumitem}

\makeatletter
\newcommand{\DoIfNonEmptyText}[1]{% Actually takes two parameters
    %\sbox0{#1}%
    \sbox0{\begingroup\vbox{#1}\endgroup}%
    \ifdim\wd0=\z@\relax% 
        \expandafter\@gobble%
    \else%
        \expandafter\@firstofone%
    \fi%
}%
\makeatother

\newcommand{\QuotableText}[3][]{%
    % [#1] = optional title
    % #2   = name for this (not relevant for this example)
    % #3   = the content
    \DoIfNonEmptyText{#1}{\textit{#1}:~}%
    #3%
}%


\begin{document}
\begin{enumerate}
    \item First Item
    \item \QuotableText[Sub title]{Text One}{Second Item}
    \item \QuotableText{Text Two}{Third Item}
    \item Fourth Item
\end{enumerate}
\end{document}

Update:

Based on Frank Mittelbach's answer, I attempted to use my own \savebox, but it still exhibits the same output as above:

\makeatletter
\newsavebox{\@DoIfNonEmptyTextBox}%
\newcommand{\DoIfNonEmptyText}[1]{% Actually takes two parameters
    \begingroup
    \savebox{\@DoIfNonEmptyTextBox}{\vbox{#1}}% -- This still gobbles the item number!
    %\savebox{\@DoIfNonEmptyTextBox}{#1}%        -- This works
    \ifdim\wd\@DoIfNonEmptyTextBox=\z@\relax% 
        \endgroup\expandafter\@gobble%
    \else%
        \endgroup\expandafter\@firstofone%
    \fi%
}%
\makeatother

but if I remove the \vbox{}, then this works just fine. So, how is the use of the \vbox (note: no use of \sbox0 here), causing the enumerate item to disappear?

Best Answer

The wrong answer (though by chance it solved the issue)

The answer is simply do not use box 0 or ensure that your use of it does no harm. This is a scratch box, but the \item command of LaTeX also uses that one to store its item for a tiny while (between encountering the \item command and typesetting the label once the paragraph started) and your \QuotableText is executed during this time.

Now your change to box0 is not local thus you overwrite the content.

Solution 1: use \newbox to get your private box register for your test

Solution 2: make your change to box0 local. You start a group and close it directly after making the measurement (which means close it in both branches):

\makeatletter
\newcommand{\DoIfNonEmptyText}[1]{% Actually takes two parameters
\begingroup
    \sbox0{#1}%
    \ifdim\wd0=\z@\relax% 
\endgroup
        \expandafter\@gobble%
    \else%
\endgroup
        \expandafter\@firstofone%
    \fi%
}%
\makeatother

You had

\sbox0{\begingroup\vbox{#1}\endgroup}%

but there the group is done at the wrong place. The setting of box 0 ist still outside the group. Inside your group there is only stuff that is being typeset and so the group has no effect whatsoever.

Update (why is the \vbox making the item label disappear)

This is a tricky one. One has to look carefully at how the list environment attaches the item label to the item paragraph.

Basically, what happens inside is that the label is constructed and stored in a box (and it is not as I claimed earlier box 0 but actually a private box (\@labels) so thanks for the vote of confidence, but my answer was simply wrong.

Now that box is attached to the by the use of \everypar, the actual definition used is

\everypar={\@minipagefalse \global \@newlistfalse \if@inlabel \global \@inlabel
false {\setbox \z@ \lastbox \ifvoid \z@ \kern -\itemindent \fi }\box \@labels 
\penalty \z@ \fi \if@nobreak \@nobreakfalse \clubpenalty \@M \else \clubpenalty 
\@clubpenalty \everypar {}\fi }

So the above gets executed when a switch from vertical mode to horizontal mode happens.

Now if we just test using \sbox then this is building an h-box and so \everyparis not executed. However, if we use a \vbox inside then any text in #1 will trigger that \everypar. And if you look carefully, the label will only be set if @inlabel is true and this switch is globally set to false in the true case.

So basically the label is put into this \vbox for testing and afterwards is not being typeset again.

So in summary the above initial argument was wrong (I didn't look carefully, the list environment does use box 0 but that is harmless) and my redefinition only worked because I took out the \vbox at the same time.

Related Question