0

I'm trying to write an index using "locations" that are not page numbers but some kind of sections (not with proper sectioning commands either). These locations are just numbers with possibly one or two letters after, like 003 or 37b (and 003 and 03 are different).

I'm using xindy, which is quite new to me, and I'm struggling to:

  • Prevent sorting the location numbers: I want them appear just in the order they appear in the document.
  • Prevent "compressing" ranges. If something appears in locations 003, 004 and 005, I want 003, 004, 005, not 003-005.
  • Play nicely with hyperref: I'd like the index to link to the page where the "location" starts (probably).

A sample document, this is as far as I got:

\documentclass{article}

\usepackage[xindy]{imakeidx}
\usepackage{hyperref}

\makeindex[options=-M foo]

\begin{filecontents}[overwrite,noheader]{foo.xdy}
(define-location-class "type"
                       ("arabic-numbers" :sep "" "alpha") :min-range-length 1000)
\end{filecontents}

\makeatletter

\def\current{}
\newcommand{\type}[1]{\def\current{#1}\textbf{#1}\par}

\renewcommand{\imki@wrindexentrysplit}[3]{%
  \expandafter\protected@write\csname#1@idxfile\endcsname{}%
    {\string\indexentry{#2}{\current}}%
}

\makeatother

\begin{document}

\type{003}

One\index{one}
Two\index{two}
Three\index{three}

\type{024}

Two\index{two}

\clearpage
\type{024b}

One\index{one}

\type{024v}

One\index{one}

\type{024f}

One\index{one}
Two\index{two}
Three\index{three}

\clearpage
\type{100}

Three\index{three}

\clearpage

One\index{one}
Three\index{three}

\clearpage
\type{01}

One\index{one}

\clearpage
\type{24}

One\index{one}
Four\index{four}

\clearpage
\type{25}

Four\index{four}

\clearpage
\type{26}

Four\index{four}

\clearpage
\type{29}

Two\index{two}

\printindex

\end{document}

Note that four appears as 24-26 instead of 24, 25, 26 (although the :min-range-length seems to work with page numbers). The locations are sorted numerically, and with those containing letters at the end. The hyperlinks are all broken.

Any hints or ideas on how to get closer to my goal?

1 Answer 1

1

Oooookay... I think I got it.

  1. alpha means a single letter, not "any number of letters".
  2. The "standard" page numbers are still defined as a location class, and they allow ranges by default.
  3. Different location classes are sorted separately, that's why all the locations without letters appear before those with letter.

So I decided to take another approach and write a more verbose "location key" that consists of:

  • an increasing counter for every location, so that sorting will be a no-op.
  • the location name
  • the page number, to be used for hyperlinks.

The location name was a bit of a challenge, because I can't find a way to specify that any number of lowercase letters are allowed. After some digging into xindy's source code, I found the undocumented (as far as I can see) define-enumeration, which is actually used to define arabic-numbers, and I extended it to allow any number of alphanumeric characters.

Once I got that right, it was just a matter of defining the proper formatting command, so that when the index is typeset I see only the location name (hyperlinked to the page). The listofitems package was very useful.

As a bonus, I tweaked the ranges formatting so that only the first page is printed. The location is the right one, but the link leads to the page where the term actually appears (nonconsecutive pages within the same location could be an issue, but I don't have such case).

\documentclass{article}

\usepackage[xindy]{imakeidx}
\usepackage{listofitems}
\usepackage{hyperref}

\makeindex[options=-M foo]

\begin{filecontents}[overwrite,noheader]{foo.xdy}
;; copied from idxstyle.lsp
(defun prefix-match-for-radix-numbers (str radix)
  (let* ((n nil)
         (strlen (length str))
         (len-match (do ((i 0 (1+ i)))
                        ((or (>= i strlen)
                             (not (digit-char-p (char str i) radix)))
                           i)
                      (setq n (+ (* (if n n 0) radix)
                                 (digit-char-p (char str i) radix))))))
    (values (subseq str 0 len-match)
            (subseq str len-match)
            n)))

;; any number of alphanumeric characters
(define-enumeration "alphanum"
                    #'(lambda (str)
                        (prefix-match-for-radix-numbers str 36))
                    "0123456789abcdefghijklmnopqrstuvwxyz")

;; prefix indicates sort order, letter "a" should be removed
(define-location-class "loc-and-page"
                       ("arabic-numbers" :sep ":" "alphanum" :sep ":" "arabic-numbers") :min-range-length 1)

;; for page ranges, use only the first one
(markup-range :sep "" :ignore-end)

;; use a custom command for location formatting
(markup-locref :attr "hyperpage" :open "\locfmt{" :close "}")
\end{filecontents}

\makeatletter

\def\locidx{}
\newcounter{type}
\setcounter{type}{0}
\newcommand{\type}[1]{%
  % increase the counter
  \stepcounter{type}%
  % define the "index key"
  \def\locidx{\arabic{type}:#1}%
  % typeset the text
  \textbf{#1}\par}

% write the "index key" and the page number to the index
\renewcommand{\imki@wrindexentrysplit}[3]{%
  \expandafter\protected@write\csname#1@idxfile\endcsname{}%
    {\string\indexentry{#2}{\locidx:\thepage}}%
}

% define the formatting command
\newcommand{\locfmt}[1]{%
  % split the location at the colons
  \setsepchar{:}\readlist\parts{#1}%
  % 1 is a counter and can be ignored
  % 2 is the actual location
  % 3 is the page number
  \hyperlink{page.\parts[3]}{\parts[2]}%
}

\makeatother

\begin{document}

\type{003}

One\index{one}
Two\index{two}
Three\index{three}

\type{024}

Two\index{two}

\clearpage
\type{024b}

One\index{one}

\type{024v}

One\index{one}

\type{024f}

One\index{one}
Two\index{two}
Three\index{three}

\clearpage
\type{100}

Three\index{three}

\clearpage

One\index{one}
Three\index{three}

\clearpage
\type{01}

One\index{one}

\clearpage
\type{24}

One\index{one}
Four\index{four}

\clearpage
\type{25}

Four\index{four}

\clearpage
\type{26}

Four\index{four}

\clearpage
\type{29vf}

Two\index{two}

\printindex

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .