6

The idea is to use the index information to automatically generate cross references. If an index entry refers to more than 1 page (e.g., "Smith, John 5, 9"), then the index entry along with the other page numbers should show up in the margin at the place where I make the index entry (i.e., following the above example, on page 5 in the margin appears "Smith, John 9").

Is it possible to change the showidx package accordingly?

In addition, the solution should be compatible with hyperref and the fact that I'm using twosided but the bigger margin is always on the right (I'm using tufte-latex).

Thanks for any hints.

And here my try of an MWE (not sure whether that helps):

%\documentclass[twoside]{tufte-book}
\documentclass{book}
\usepackage{makeidx,showidx,lipsum}
\makeindex
\begin{document}
John Smith\index{Smith, John}
\lipsum
John Smith\index{Smith, John}
\lipsum
\printindex
\end{document}
  • Looks interesting. Can you post a MWE? – Jagath Jul 16 '13 at 3:54
  • added MWE, maybe it needs just a small tweak of showidx to give the page numbers? – Pisco Sour Jul 16 '13 at 7:58
3

The "small tweak" is quite an understatement. After the usual processing of the .idx file by makeindex, the relation of the raw data inside \index (LaTeX file) or \indexentry (.idx file) to the formatted index entry in the .ind file gets lost. Example:

\index{foobar}         ⇒ \item foobar, ...
\index{foo@foobar}     ⇒ \item foobar, ...          (sort spec.
\index{foobar|(}       ⇒ \item foobar, ...          (range spec.)
\index{foobar|textbf}  ⇒ \item foobar, \textbf{1}   (encap feature)
\index{"f"o"o"b"a"r}   ⇒ \item foobar, ...          (quote char)
\index{foo!bar}        ⇒ \item foo ...\subitem bar  (level char)
\index{fooX@foo!barY@b"ar|(emph}
                        ⇒ \item foo ...\subitem bar  (sort + level + quote + range + encap)

From the generated index file, the exact form of \index can only be guessed (e.g. \index{foobar} and \index{foo!bar}. The sort, range, encap specifications are gone. Thus the result from the generated index file would be a mapping from such keys to the page list, e.g.:

foobar  ⇒ 1, \textbf{2}, 3
foo!bar ⇒ 3--7, 10

The following package indexpagelist defines a style file indexpagelist.ist for makeindex that generates such a mapping.

Back at the LaTeX level, we need to strip the sort, range and encap specifications from the string, that is given to \index.

To reduce the level of complexity and to save some time, I have not implemented the support for quote and escape chars. Also the actual, level, and encap chars are only supported with their default values (@, !, |).

%%% indexpagelist.sty %%%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{indexpagelist}[2013/07/16 v0.1 Index page list for showidx] 
\RequirePackage{filecontents}
\begin{filecontents*}{indexpagelist.ist}
preamble ""
postamble ""
setpage_prefix ""
group_skip ""
headings_flag 0
symhead_negative ""
numhead_negative ""
item_0 "\\iplItemA{"
item_1 "\\iplItemB{"
item_2 "\\iplItemC{"
item_01 "\\iplItemB{"
item_x1 "}\n\\iplItemB{"
item_12 "\\iplItemC{"
item_x2 "}\n\\iplItemC{"
delim_0 "}\n\\iplPageList{"
delim_1 "}\n\\iplPageList{"
delim_2 "}\n\\iplPageList{"
delim_n "\\iplMultiPage, "
delim_r "\\iplMultiPage--"
delim_t "}\n"
line_max 1000
\end{filecontents*}
\immediate\write18{%
  makeindex %
  -s indexpagelist.ist %
  -t \jobname-ipl.ilg %
  -o \jobname.ipl %
  \jobname.idx%
}
\immediate\write18{makeindex \jobname}% for convenience

\newcommand*{\iplItemA}[1]{%
  \def\iplCurrentItemA{#1}%
  \@onelevel@sanitize\iplCurrentItemA
  \let\iplCurrent\iplCurrentItemA
}
\newcommand*{\iplItemB}[1]{%
  \def\iplCurrentItemB{#1}%
  \@onelevel@sanitize\iplCurrentItemB
  \edef\iplCurrent{\iplCurrentItemA!\iplCurrentItemB}%
}
\newcommand*{\iplItemC}[1]{%
  \def\iplCurrentItemC{#1}%
  \@onelevel@sanitize\iplCurrentItemC
  \edef\iplCurrent{\iplCurrentItemA!\iplCurrentItemB!\iplCurrentItemC}%
}
\newcommand*{\iplPageList}[1]{%
  \expandafter\@ipl@PageList#1\iplMultiPage\@nil
}
\newcommand*{\iplMultiPage}{}
\def\@ipl@PageList#1\iplMultiPage#2\@nil{%
  \def\@ipl@Temp{#2}%
  \ifx\@ipl@Temp\@empty
  \else
    \expandafter\protected@xdef\csname ipl@\iplCurrent\endcsname{#1#2}%
  \fi
}
\InputIfFileExists{\jobname.ipl}{}{}

\newcommand*{\iplGetPageList}[1]{%
  \def\@ipl@Temp{#1}%
  \@onelevel@sanitize\@ipl@Temp
  \let\@ipl@Key\@empty
  \expandafter\@ipl@GetPageList@Encap\@ipl@Temp|\@nil
  \@ifundefined{ipl@\@ipl@Key}{}{%
    , \@nameuse{ipl@\@ipl@Key}%   
  }%
}   
\def\@ipl@GetPageList@Encap#1|#2\@nil{%
  \let\@ipl@Key\@empty
  \@ipl@GetPageList@Level#1!\@nil
}
\def\@ipl@Temp#1{%
  \def\@ipl@GetPageList@Level##1!##2\@nil{%
    \@ipl@GetPageList@Sort##1#1\@nil
    \def\@ipl@Temp{##2}%
    \ifx\@ipl@Temp\@empty
    \else
      \@ipl@GetPageList@Level##2\@nil
    \fi
  }%   
  \def\@ipl@GetPageList@Sort##1#1##2\@nil{%
    \def\@ipl@Temp{##2}%
    \edef\@ipl@Key{%
      \ifx\@ipl@Key\@empty
      \else
        \@ipl@Key!%
      \fi
      \ifx\@ipl@Temp\@empty
        ##1%
      \else 
        \@ipl@RemoveSortChar##2\@nil
      \fi
    }%   
  }%     
  \def\@ipl@RemoveSortChar##1#1\@nil{##1}%
}
\expandafter\@ipl@Temp\string @
% remaining "small tweak" for package showidx
\newcommand*{\iplPatchShowidx}{%
  \@ifpackageloaded{showidx}{%  
    \@ifdefinable{\saved@showidx}{%
      \let\saved@showidx\@showidx  
      \renewcommand*{\@showidx}[1]{%
        \saved@showidx{##1\iplGetPageList{##1}}%
      }%
    }%  
    \global\let\iplPatchShowidx\relax
  }{}%
}
\iplPatchShowidx
\AtBeginDocument{\iplPatchShowidx}
\endinput

Then the "tweak" to the main TeX file is tiny, only the package needs to be loaded. Because of deficiencies in the OT1 encoding, the encap char | is displayed as em dash , therefore I have used T1 font encoding with Latin Modern fonts.

%%% test.tex %%%
\documentclass{book}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{makeidx,showidx,lipsum,indexpagelist}
\makeindex

\begin{document}
John Smith\index{Smith, John}, John Doe\index{Doe, John},
cat\index{animals!cat|(}, dog\index{animals!dog}
\lipsum
John Smith\index{Smith, John}, dog\index{animals!dog|textbf},
Donald E. Knuth\index{Knuth, Donald E.}
\lipsum
cat\index{animals!cat|)}, dog\index{animals!dog},
Donald E. Knuth\index{Knuth, Donald E.}
\newpage
\printindex
\end{document}

The raw index file test.idx:

\indexentry{Smith, John}{1}
\indexentry{Doe, John}{1}
\indexentry{animals!cat|(}{1}
\indexentry{animals!dog}{1}
\indexentry{Smith, John}{2}
\indexentry{animals!dog|textbf}{2}
\indexentry{Knuth, Donald E.}{2}
\indexentry{animals!cat|)}{3}
\indexentry{animals!dog}{3}
\indexentry{Knuth, Donald E.}{3}

The main index file is generated as usual:

$ makeindex test

The index file test.ind for \printindex:

\begin{theindex}

  \item animals
    \subitem cat, 1--3
    \subitem dog, 1, \textbf{2}, 3

  \indexspace

  \item Doe, John, 1

  \indexspace

  \item Knuth, Donald E., 2, 3

  \indexspace

  \item Smith, John, 1, 2

\end{theindex}

The file with the page list mappings is generated by:

$ makeindex -s indexpagelist.ist -t test-ipl.ilg -o test.ipl test.idx

For convenience I have added both calls of makeindex via \immediate\write18 in the package file (TeX Live executes them with restricted shell escape).

The file test.ipl contains the mappings from the index keys to their page lists:

iplItemA{animals}
\iplItemB{cat}
\iplPageList{1\iplMultiPage--3}
\iplItemB{dog}
\iplPageList{1\iplMultiPage, \textbf{2}\iplMultiPage, 3}
\iplItemA{Doe, John}
\iplPageList{1}
\iplItemA{Knuth, Donald E.}
\iplPageList{2\iplMultiPage, 3}
\iplItemA{Smith, John}
\iplPageList{1\iplMultiPage, 2}

That defines macros such as \ipl@animals!dog with contents 1, \textbf{2}, 3. Macro \iplMultiPage is used as marker to detect single and multi page lists more easily.

Then the package hooks into \@showidx to add the page list.

The index:

index

Page 1:

page 1

Page 2:

page 2

Page 3:

page 3

  • Wow - great! OK, it's not a small tweak ;) Your listing of indexpagelist.sty seems slightly faulty: in line 28 I'm missing 'me}' (maybe too long line?). Having fixed that, it works. Thank you! Any idea on how to enforce that it always appears on the right margin even with twosided (that's my tufte-book problem...)? – Pisco Sour Jul 16 '13 at 15:10
  • @PiscoSour: Thanks, I fixed the truncated line, it was indeed a copy and paste error. – Heiko Oberdiek Jul 16 '13 at 15:22
  • @PiscoSour: Quick and dirty: \usepackage{showidx} \makeatletter\let\@leftidx\@rightidx\makeatother. – Heiko Oberdiek Jul 16 '13 at 15:26
  • Wow! Did you just come up with indexpagelist to solve this question, or was it something you already had lying around? I've been able to make use of it, but I'm wondering if you have some thoughts on getting it to work with hyperref as it doesn't seem to like \hyperpage{pagenum}. – KevinG Jan 10 '18 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.