1

I am using imakeidx. How do I group entries in the (person) index in a way that they do not get split across page boundaries? My goal here is to have groups of surnames "Smith" and a group of "Jones", plus 1-12 first names each, in the index, and keep those groups on the same page.

I have the feeling that some application of \nopagebreak might solve it, but where?

Here is a pdflatex MWE and below an image of the output, with a big red rectangle as annotation to show where the surname in index gets separated from the several first names. All the "Wilson" names should be on same page.

The MWE is pdflatex file:

\documentclass{article}

\usepackage{imakeidx}

\makeindex[name=person, title={Index},options=-s example.ist, columns=1] % Use .ist for dotfill

\makeindex

\begin{document} Page1

\index[person]{{Smith}!{John1 , something}} \index[person]{{Smith}!{John2 , something}} \index[person]{{Smith}!{John3 , something}}

\index[person]{{Johnson}!{John1 , something}} \index[person]{{Johnson}!{John2 , something}} \index[person]{{Johnson}!{John3 , something}}

\index[person]{{Williams}!{John1 , something}} \index[person]{{Williams}!{John2 , something}} \index[person]{{Williams}!{John3 , something}}

\index[person]{{Brown}!{John1 , something}} \index[person]{{Brown}!{John2 , something}} \index[person]{{Brown}!{John3 , something}}

\index[person]{{Jones}!{John1 , something}} \index[person]{{Jones}!{John2 , something}} \index[person]{{Jones}!{John3 , something}}

\index[person]{{Miller}!{John1 , something}} \index[person]{{Miller}!{John2 , something}} \index[person]{{Miller}!{John3 , something}}

\index[person]{{Davis}!{John1 , something}} \index[person]{{Davis}!{John2 , something}} \index[person]{{Davis}!{John3 , something}}

\index[person]{{Wilson}!{John1 , something}} \index[person]{{Wilson}!{John2 , something}} \index[person]{{Wilson}!{John3 , something}}

\index[person]{{Taylor}!{John1 , something}} \index[person]{{Taylor}!{John2 , something}} \index[person]{{Taylor}!{John3 , something}}

\index[person]{{Martinez}!{John1 , something}} \index[person]{{Martinez}!{John2 , something}} \index[person]{{Martinez}!{John3 , something}}

\index[person]{{Thompson}!{John1 , something}} \index[person]{{Thompson}!{John2 , something}} %\index[person]{{Thompson}!{John3 , something}}

\index[person]{{Lee}!{John1 , something}} \index[person]{{Lee}!{John2 , something}} \index[person]{{Lee}!{John3 , something}}

\printindex[person]

\end{document}

which uses the one line style example.ist

delim_1 "\\nobreak\\dotfill "

to achieve the nice dot fill. All Wilson's should be on the same page pls

All surnames and first names grouped nicely, good. However, the name "Wilson" (and its three first names) is split across two pages. How do I that?

I tried in the .ist style to add \nopagebreak but nothing has the desired effect. For example

% group_skip "\n\n \\nopagebreak\\indexspace\n" 
% item_1 "\\par\\nopagebreak" 
% item_0 "\\nopagebreak" 
% prefix "\\par\\nopagebreak" 
% delim_1 "\\nobreak\\nopagebreak\\dotfill "
% delim_1 "\\nobreak\\par\\nopagebreak\\dotfill "
% delim_1 "\\nopagebreak\\dotfill "
% delim_1 "\\nolinebreak\\dotfill "

How to massage the .ist style to keep the groups of surnames plus 1-12 first names on the same page? In this case, I would expect an extra empty line of page 2 and "Wilson" plus 3 first names on page as a group.

PS: While I look at the initial comments, I realize that the example above concentrates on 1-12 sub-items (first names), the full latex file has a few instances with almost 100 sub-items (first names) spanning more than a page which leads to additional challenges. Ideally, a solution would handle the whole range.

Just a wild guess, but if we choose a threshold N and can have all groups of N together and split larger ones, would that work? Maybe something simple like

if there are less than N==5 sub-items/names 
then keep them together
else do nothing and break as usual 

might work? But how to code that?

PS2: Thank you for all your help so far. In the first answer, a solution is suggested that works it seems, but not for wrapped lines in index. Am adding here a second MWE to demonstrate and a second image to visualize the issue.

\documentclass{article}

\usepackage{imakeidx}

% ---- \makeatletter \newcommand{\nobreakafteritem}{} \newcommand{\setnobreakafteritem}{\renewcommand{\nobreakafteritem}{\par\penalty10000\relax \renewcommand{\nobreakafteritem}{}}} \usepackage{etoolbox}\pretocmd\subitem{\nobreakafteritem}{}{\fail} \usepackage{etoolbox}\pretocmd@idxitem{\setnobreakafteritem}{}{\fail} \makeatother %----------------

\makeindex[name=person, title={Index},options=-s example.ist, columns=1] % Use .ist for dotfill

\makeindex

\begin{document} Page1

\index[person]{{Smith}!{John1 , something}} \index[person]{{Smith}!{John2 , something}} \index[person]{{Smith}!{John3 , something}}

\index[person]{{Johnson}!{John1 , something}} \index[person]{{Johnson}!{John2 , something}} \index[person]{{Johnson}!{John3 , something}}

\index[person]{{Williams}!{John1 , something}} \index[person]{{Williams}!{John2 , something}} %\index[person]{{Williams}!{John3 , something}} %ORG \index[person]{{Williams}!{John3 , something very long wrap the line extra long and longer and longer and longer}}

\index[person]{{Brown}!{John1 , something}} \index[person]{{Brown}!{John2 , something}} \index[person]{{Brown}!{John3 , something}}

\index[person]{{Jones}!{John1 , something}} \index[person]{{Jones}!{John2 , something}} \index[person]{{Jones}!{John3 , something}}

\index[person]{{Miller}!{John1 , something}} \index[person]{{Miller}!{John2 , something}} \index[person]{{Miller}!{John3 , something}}

\index[person]{{Davis}!{John1 , something}} \index[person]{{Davis}!{John2 , something}} \index[person]{{Davis}!{John3 , something}}

\index[person]{{Wilson}!{John1 , something}} \index[person]{{Wilson}!{John2 , something}} %\index[person]{{Wilson}!{John3 , something}}

\index[person]{{Taylor}!{John1 , something}} \index[person]{{Taylor}!{John2 , something}} \index[person]{{Taylor}!{John3 , something}}

\index[person]{{Martinez}!{John1 , something}} \index[person]{{Martinez}!{John2 , something}} \index[person]{{Martinez}!{John3 , something}}

\index[person]{{Thompson}!{John1 , something}} \index[person]{{Thompson}!{John2 , something}} \index[person]{{Thompson}!{John3 , something}} %

\index[person]{{Lee}!{John1 , something}} \index[person]{{Lee}!{John2 , something}} \index[person]{{Lee}!{John3 , something}}

\printindex[person]

\end{document}

for the PS2, an example of a wrapped line in index that splits over 2 pages

Ingmar
  • 6,690
  • 5
  • 26
  • 47
Hans
  • 11
  • \usepackage{etoolbox}\pretocmd\subitem{\par\nobreak}{}{\fail} could work. – Ulrike Fischer Apr 19 '21 at 20:49
  • Firstly, that solves this example. Thanks! That encouraged me to try a few more variants. In doing that, this solution seems to have unexpected effects when the number of first names is very large, like more than the number of lines on a page.

    Therefore, secondly, would you know how to generalize this when we have not only between 1 and a dozen or so but, e.g., between 1 and 100 first names per surname? And 100 just chosen as a number larger than the number of lines in the index page.

    – Hans Apr 19 '21 at 21:41
  • Looking at https://tex.stackexchange.com/questions/21983/how-to-avoid-page-breaks-inside-paragraphs I tried this \usepackage{etoolbox}\pretocmd\subitem{\par\widowpenalty 10000\raggedbottom}{}{\fail} but I do not know how to set the penalties such that groups of 1 to 10 stay to together and larger groups can be broken up. – Hans Apr 19 '21 at 22:09
  • well if you have many subitems and want to break between them it will get a bit more complicated. – Ulrike Fischer Apr 19 '21 at 22:12

2 Answers2

0

A completely untested possibility:

\makeatletter
\newcommand{\nobreakafteritem}{}
\newcommand{\setnobreakafteritem}{\renewcommand{\nobreakafteritem}{\par\penalty10000\relax
  \renewcommand{\nobreakafteritem}{}}}
\pretocmd\subitem{\nobreakafteritem}{}{\fail}
\pretocmd\@idxitem{\setnobreakafteritem}{}{\fail}
\makeatother

The basic idea: After an \item in the index (which is generated by \@idxitem), define \nobreakafteritem to put a no break penalty, but then after its first invocation, it reverts to a no-op. Then patch \subitem to call \nobreakafteritem. It would be possible to have \nobreakafteritem self-destruct after a set number of invocations, or perhaps even lower the penalty with each invocation until it goes down to zero.

Don Hosek
  • 14,078
  • Sounds intriguing. When I tried this as-is today, there is an error ````Runaway argument? {\renewcomand {\nobreakafteritem }{\par \penalty 10000\relax \renewcomand \ETC. ! File ended while scanning use of @argdef. \par <*> index_example.tex ```` Seems like a bracket is missing? My best guesses where not able to get this to work probably. Therefore, can you help please to make this code snippet run properly for testing ? Thanks. PS: Don't we need a \usepackage{etoolbox} before the pretocmd ? – Hans Apr 20 '21 at 13:26
  • Ok,I managed to get this working with three edits (1) "renewcomand" is misspelled and needs an extra 'm' (2) added an extra curly bracket after "renewcomand{\nobreakafteritem}{} } }" (Q: Was that the correct fix?) and (3) use the 'etoolbox'. – Hans Apr 20 '21 at 15:12
  • While that solves the grouping of surnames plus first names the MWE (thanks!), there are two issue with this approach (1) it destroy the right alignment of the '....1' (see original picture for expected output). All the page numbers "1" are not in the same right column any more. And (2) change the MWE line \index[person]{{Williams}!{John3 , something very long wrap the line extra long and longer and longer and longer}} to see that such a wrapped, long index line (if it is the last one at the bottom of page) is NOT grouped correctly. – Hans Apr 20 '21 at 15:12
  • Thoughts on how to proceed? – Hans Apr 20 '21 at 15:14
  • @Hans I'll take a look later today. – Don Hosek Apr 20 '21 at 15:45
  • @Hans I fixed the typos in my original solution. You added a space before the } you added which is where the extra space came from. I'm not sure what the very long line issue is—when I replaced the John3 Williams line with yours, I'm seeing what looks like the correct output. – Don Hosek Apr 20 '21 at 20:37
  • Thanks for that. Didn't realize the whitespace would be significant there, ouch :) Now that the right alignment is sorted again, I'd like to fix the wrapped line example as well please. I'll extend the original question with a PS2, a MWE #2, and an image to reproduce. – Hans Apr 21 '21 at 14:21
  • @Hans I think the wrapped line issue might be something that calls for a new question. I think it's really a different issue. – Don Hosek Apr 21 '21 at 14:25
0

Add item_01 "\\nopagebreak\n \\subitem " to your .ist file to insert a \nopagebreak between items on level 0 (main) and level 1 (sub).

Add item_1 "\\nopagebreak\n \\subitem " to your .ist file to insert a \nopagebreak between items on level 1 (sub) and level 1 (sub).

Add item_x1 "\\nopagebreak\n \\subitem " to your .ist file to insert a \nopagebreak between items on level 0 (main) without associated page numbers and level 1 (sub).

The "\n \\subitem " is what is inserted by default in these places, so what the above does is purely inserting \\nopagebreak before starting the next subitem.

Here is a list of the keys (like item_01) you can use in the .ist file.