7

In reference to the question asked last week on how to deal with different kinds of index-entries: Makeindex and different kinds of references

I wondered if it is possible to up the ante. Again this is the section of Makeindex i'm referring to:

In some indexes, certain page numbers are specially formatted—for example, an italic page number may indicate the primary reference, and an n after a page number may denote that the item appears in a footnote on that page.

Only that I want to show only references to notes if no primary reference is on that page. If there is a primary reference and a note-reference on the same page everybody using the index will be satisfied with the primary one, so no need for repetition.

The output of (using egregs solution from last week):

\documentclass{scrartcl} 
\usepackage[T1]{fontenc} 
\usepackage[ngerman]{babel} 
\usepackage{makeidx}
\usepackage{xparse}

\makeindex 

\ExplSyntaxOn
\NewDocumentCommand{\nn}{m}
 {
  % split the argument at commas
  \seq_set_split:Nnn \l_martin_nn_in_seq { , } { #1 }
  % add a trailing n to each item
  \seq_set_map:NNn \l_martin_nn_out_seq \l_martin_nn_in_seq { ##1n }
  % output the items separated, if necessary, by "comma space"
  \seq_use:Nn \l_martin_nn_out_seq {,~}
 }
\seq_new:N \l_martin_nn_in_seq
\seq_new:N \l_martin_nn_out_seq
\ExplSyntaxOff

\begin{document} 

bla\index{Lahm} blablab\index{Lahm|nn}

bla\index{A} bla\index{A|nn}

bla\index{B} bla\index{B|nn}

\newpage

bla\index{Lahm|nn} blablab

bla\index{B|nn}

\newpage

bla\index{B|nn}

\printindex 

\end{document}

should not be:

A 1, 1n

B 1–3n

Lahm 1, 1n, 2n

though:

A 1

B 1–3n

Lahm 1, 2n

Is that even possible?

  • I suspect you probably got multiple encap warnings from makeindex. Are you happy to try xindy instead? – Nicola Talbot Apr 5 '17 at 14:04
  • I certainly would. – Martin Mueller Apr 5 '17 at 14:25
6
+50

The problem here is that makeindex can't deal with conflicting encaps (the location formatting command) for the same location. For example, page (location) 1 has an empty encap (from \index{Lahm}) and the encap nn (from \index{Lahm|nn}). Since makeindex doesn't know how to resolve this, it adds both 1 and \nn{1} to the location list for Lahm and issues a warning in the transcript. Assuming the file is called test.tex, then the transcript file is test.ilg and contains three such warnings:

Generating output file test.ind....
## Warning (input = test.idx, line = 4; output = test.ind, line = 3):
   -- Conflicting entries: multiple encaps for the same page under same key.

## Warning (input = test.idx, line = 6; output = test.ind, line = 7):
   -- Conflicting entries: multiple encaps for the same page under same key.

## Warning (input = test.idx, line = 2; output = test.ind, line = 11):
   -- Conflicting entries: multiple encaps for the same page under same key.
done (13 lines written, 3 warnings).

There's no easy way to fix this with makeindex other than to process test.idx after the latex call and before the makeindex call using some form of script. (The makeglossaries perl script that comes with the glossaries package does this if it detects makeindex's multiple encap warning.)

The simplest solution is to switch to xindy, which resolves this conflict by giving preference to the formatting command (encap value) according to its position in the list of defined attributes. There are two attributes in your MWE: default (the default used when the | part is omitted from \index) and nn. These need to be defined in a xindy module (.xdy file). For example:

(define-attributes (( "default" "nn" )))

This gives the default attribute a higher priority. Alternatively:

(define-attributes (( "nn" "default" )))

This gives the nn attribute a higher priority. Unfortunately, texindy (the usual method of calling xindy when used with packages like makeidx) defines "default" in the latex-loc-fmts.xdy module, so both the above will cause the warning:

Loading module "latex-loc-fmts.xdy"...
WARNING: ignoring redefinition of attribute "default" in
(DEFINE-ATTRIBUTES (("default" "textbf" "textit" "hyperpage")))

However, if we make sure the custom .xdy module is loaded first, that will ensure the custom attribute list takes precedence.

There is another problem here. In the case of the B in your example, the \index{B|nn} entry will be discarded in favour of the \index{B} entry on page 1 when the default attribute overrides the nn attribute. This means that the location list for B will end up as 1, \nn{2}, \nn{3} (since \nn{1} has been discarded it can't form a range with \nn{2} and \nn{3}). This can be fixed with a merge rule:

(merge-to "default" "nn" :drop)

You also need to tell xindy what to do with the nn attribute:

(markup-locref :open "\nn{" :close "}" :attr "nn")

This just encapsulates the location with \nn (which is what makeindex does automatically). The default attribute doesn't need any special formatting.

Finally, the location list separators need to be defined. For example, to approximately replicate makeindex:

(markup-locref-list :sep ", ")
(markup-range :sep "--")

However, there's a difference between makeindex and xindy ranges where there is an encap value. With makeindex, your MWE encapsulates the range (for example, \nn{1--3}) whereas xindy encapsulates the range elements instead (for example, \nn{1}--\nn{3}). Similarly for consecutive encaps (for example, \nn{1, 2} with makeindex vs \nn{1}, \nn{2} for xindy). This means that the range will end up appearing as 1n--3n with xindy rather than 1--3n. Since the \nn command now just encapsulates a single location, the definition is much simpler:

\newcommand{\nn}[1]{#1n}

but the range formation needs adjusting to prevent the n prefix from being applied to the starting location.

One way to get around it is to modify the range separator so that instead of simply separating ranges with -- it uses a formatting command \range{start}{end}:

(markup-range :open "\range{" :sep "}{" :close "}")

This will change \nn{1}--\nn{3} to \range{\nn{1}}{\nn{3}}. Now \range can be defined to locally alter \nn within the scope of the start value:

\newcommand{\range}[2]{{\def\nn##1{##1}#1}--#2}

Here's the complete MWE:

\documentclass{scrartcl} 
\usepackage{filecontents}
\usepackage[T1]{fontenc} 
\usepackage[ngerman]{babel} 
\usepackage{makeidx}

\begin{filecontents*}{\jobname.xdy}
; list of allowed attributes

(define-attributes (( "default" "nn" )))

; define format to use for locations

(markup-locref :open "\nn{" :close "}" :attr "nn")

; location list separators

(markup-locref-list :sep ", ")
(markup-range :open "\range{" :sep "}{" :close "}")

(merge-to "default" "nn" :drop)

\end{filecontents*}

\makeindex 

\newcommand{\nn}[1]{#1n}

\newcommand{\range}[2]{{\def\nn##1{##1}#1}--#2}

\begin{document} 

bla\index{Lahm} blablab\index{Lahm|nn}

bla\index{A} bla\index{A|nn}

bla\index{B} bla\index{B|nn}

\newpage

bla\index{Lahm|nn} blablab

bla\index{B|nn}

\newpage

bla\index{B|nn}

\printindex 
\end{document}

Assuming this file is called test.tex then the build process is:

pdflatex test
xindy -M test -M texindy -C utf8 -L english -t test.ilg test.idx
pdflatex test

Alternatively (since you're using ngerman):

pdflatex test
xindy -M test -M texindy -C din5007-utf8 -L german -t test.ilg test.idx
pdflatex test

Note that the order of the -M switches is important. This ensures that the attribute list in test.xdy comes before the attribute list in the texindy modules. (However, this only matters if you need to change the precedence of the attributes defined by the texindy modules.)

image of index

Index

A

A, 1

B

B, 1–3n

L

Lahm, 1, 2n

You can remove the letter group headings by adding the following to your document:

\newcommand*\lettergroup[1]{}

If you need any more encaps, you can just add them to the list of defined attributes, so to add ii from your comment:

(define-attributes (( "default" "nn" "ii")))

(This makes the default and nn encaps override ii.) You don't even need to define \ii (although you still can if you like). For example:

(markup-locref :open "\emph{" :close "}" :attr "ii")

(which just uses \emph directly) or

(markup-locref :open "\ii{" :close "}" :attr "ii")

(which uses \ii).

  • wonderful! i hoped this wouldn't happen and I could move on from there myself but i'm already stuck on the next step. in fact i have three different kind of index entries, within the text direct ones and italic ones for implicit references (my command is \newcommand{\ii}{\emph{#1} ) and index entries in notes. is it possible to implement this? – Martin Mueller Apr 5 '17 at 20:37
  • 1
    @MartinMueller Yes, just add the extra encap values to the list in define-attributes and provide a corresponding markup-locref line. I've edited my answer with the code for your ii encap. – Nicola Talbot Apr 5 '17 at 21:38
  • thank you! i made it that far but the "drop"-part does not work: if I add: (merge-to "default" "ii" :drop) and try it on this example: \index{person|ii}Person \newpage \index{person}\index{person|nn}\index{person|ii}Person \newpage \index{person}\index{person|nn}\index{person|ii}Person it ends up with three pages in italics. – Martin Mueller Apr 6 '17 at 7:42
  • 1
    @MartinMueller How about (merge-to "nn" "ii" :drop) instead? – Nicola Talbot Apr 6 '17 at 13:07
  • Incredible job! It works! – Martin Mueller Apr 6 '17 at 13:28

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.