How can I prevent a column break before the first sub-entry in the index?

The solution is to move \nobreak after \@idxitem:

% arara: lualatex
% arara: makeindex
% arara: lualatex

\documentclass{article}
\usepackage{makeidx}

\makeatletter
% we don't want a page break before a subitem
\renewcommand\subitem{\@idxitem\nobreak\hspace*{20\p@}}
\makeatother

\makeindex
\begin{document}
\index{gap!radial}\index{gap!axial}
\index{gapa!radial}\index{gapa!axial}
\index{gapb!radial}\index{gapb!axial}
\index{gapc!radial}\index{gapc!axial}
\index{gapd!radial}\index{gapd!axial}
\index{gap1!radial}\index{gap1!axial}
\index{gapa1!radial}\index{gapa1!axial}
\index{gapb1!radial}\index{gapb1!axial}
\index{gapc1!radial}\index{gapc1!axial}
\index{gapd1!radial}\index{gapd1!axial}
\index{gap2!radial}\index{gap2!axial}
\index{gapa2!radial}\index{gapa2!axial}
\index{gapb2!radial}\index{gapb2!axial}
\index{gapc2!radial}\index{gapc2!axial}
\index{gapd2!radial}\index{gapd2!axial}
\printindex
\end{document}

enter image description here

The standard definition of \subitem is without \nobreak; the command \@idxitem just does a \par and sets \hangindent, so if we add the penalty (\nobreak is \penalty 10000) after it, TeX will not break a page at this point.


If you just want that the first subitem remains attached to the main entry, then change the code between \makeatletter and \makeatother into

\makeatletter
% we don't want a page break before the first subitem
\newif\iffirst@subitem
\def\@idxitem{%
  \par\hangindent40\p@ % original
  \first@subitemtrue   % added
}
\def\subitem{%
  \par\hangindent40\p@
  \iffirst@subitem
    \nobreak
    \first@subitemfalse
  \fi
  \hspace*{20\p@}}
\makeatother

This will add the penalty only between a main item and the first subitem.


I'm working with lengthy index for a textbook. I found it useful implement egreg's suggestion using LaTeX's renewcommand, which I could drop into the preamble. This code also suppresses breaks before first subsubitem and keeps \see entries on the same page. (The \see fix is somewhat experimental, as I'm not sure it will be appropriate for longer entries, but it does eliminate widows...)

    \newif\iffirst@subitem
    \newif\iffirst@subsubitem\first@subsubitemfalse
    \renewcommand\@idxitem{\par\hangindent 40\p@ \first@subitemtrue}
    \renewcommand\subitem{\par\hangindent 
           40\p@\iffirst@subitem\nobreak\first@subitemfalse\fi 
           \hspace*{20\p@}\first@subsubitemtrue}
    \renewcommand\subsubitem{\par\hangindent 
           40\p@\iffirst@subsubitem\nobreak\first@subsubitemfalse\fi 
           \hspace*{30\p@}}
    \renewcommand\see[2]{\samepage\emph{\seename} #1}