texlive[74328] branches/branch2024.final/Master: indextra (branch)

commits+karl at tug.org commits+karl at tug.org
Thu Feb 27 21:15:53 CET 2025


Revision: 74328
          https://tug.org/svn/texlive?view=revision&revision=74328
Author:   karl
Date:     2025-02-27 21:15:53 +0100 (Thu, 27 Feb 2025)
Log Message:
-----------
indextra (branch) (27feb25)

Modified Paths:
--------------
    branches/branch2024.final/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc

Added Paths:
-----------
    branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/
    branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/README.md
    branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc-demo.tex
    branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.pdf
    branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.tex
    branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra.xdy
    branches/branch2024.final/Master/texmf-dist/makeindex/indextra/
    branches/branch2024.final/Master/texmf-dist/makeindex/indextra/indextra.ist
    branches/branch2024.final/Master/texmf-dist/source/latex/indextra/
    branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.dtx
    branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.ins
    branches/branch2024.final/Master/texmf-dist/tex/latex/indextra/
    branches/branch2024.final/Master/texmf-dist/tex/latex/indextra/indextra.sty
    branches/branch2024.final/Master/tlpkg/tlpsrc/indextra.tlpsrc

Added: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/README.md
===================================================================
--- branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/README.md	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/README.md	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,15 @@
+# `indextra` - Enhanced index typesetting
+
+## Description
+
+This package provides some enhanced features for typesetting indexes, notably: (1) Continuation text when entries or
+sub-entries continue from one page or column to the next. (2) An interface for accessing marks created from index
+entries, so that (for example) a running head can include the range of index entries that appears on the page.
+
+## Author
+
+This package is by Alan J. Cain: a.j.cain (AT) gmail.com
+
+## Licence
+
+Released under the LaTeX Project Public License v1.3c or later: https://www.latex-project.org/lppl.txt


Property changes on: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc-demo.tex
===================================================================
--- branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc-demo.tex	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc-demo.tex	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,89 @@
+%%
+%% This is file `indextra-doc-demo.tex',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% indextra.dtx  (with options: `demo')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2025 Alan J. Cain
+%% 
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%% 
+%% http://www.latex-project.org/lppl.txt
+%% 
+%% and version 1.3c or later is part of all distributions of
+%% LaTeX version 2008-05-04 or later.
+%% 
+\documentclass{article}
+
+\usepackage[
+paperwidth=105mm,
+paperheight=100mm,
+inner=5mm,
+width=90mm,
+height=90mm,
+top=5mm,
+nohead,
+nofoot,
+columnsep=5mm,
+twocolumn,
+]{geometry}
+
+\usepackage{xcolor}
+\definecolor{bg}{gray}{0.9}
+\pagecolor{bg}
+
+\parindent=1em
+
+\usepackage{indextra}
+
+\indextrasetup{
+  before code={},
+  after code={},
+}
+
+\newcommand\see[1]{\textit{see}~`#1'}
+\newcommand\seealso[1]{\textit{see also}~`#1'}
+
+\raggedright
+\pagestyle{empty}
+
+\begin{document}
+
+\begin{theindextra}
+  \indextrastyleversion{0.1}
+  \indextraprocessortype{ist}
+
+  \indextraentry{0}{neutron}{855, 862, 885--886}%
+  \indextraentry{0}{\textit{New Astronomy} (Kepler)}{\see{\textit{Astronomia Nova}}{}}%
+  \indextraentry{0}{Newtonian physics}{298, 376, 390, 589, 813, 830, 834, 842, 891, 894--895}%
+  \indextraentry{0}{\textit{Nicomachean Ethics} (Aristotle)}{85}%
+  \indextraentry{0}{Nim}{11}%
+  \indextraentry{0}{nine-point circle}{584, 641, 662--663}%
+  \indextraentry{0}{Nobel Prize}{}%
+    \indextraentry{1}{chemistry}{495, 566, 815, 865, 937}%
+    \indextraentry{1}{literature}{485}%
+    \indextraentry{1}{peace}{865}%
+    \indextraentry{1}{physics}{502, 598, 727, 834, 836, 838, 851, 853--854, 856, 865, 867, 877, 893, 903, 905, 918, 921}%
+  \indextraentry{0}{nobility}{188, 240, 250, 464}%
+  \indextraentry{0}{non-aesthetic property}{700, 715}%
+  \indextraentry{0}{non-associative algebra}{848}%
+  \indextraentry{0}{non-commutativity}{643}%
+  \indextraentry{0}{non-constructive proof}{787}%
+  \indextraentry{0}{non-determinism}{595}%
+  \indextraentry{0}{non-euclidean geometry}{380, 401, 404--408, 436, 628, 643, 880, 896}%
+  \indextraentry{0}{non-linearity}{882}%
+  \indextraentry{0}{non-measurable set}{672}%
+  \indextraentry{0}{non-sensory property}{695}%
+  \indextraentry{0}{non-visual thinking}{633}%
+  \indextraentry{0}{nonagon}{220}%
+
+\end{theindextra}
+
+\end{document}


Property changes on: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc-demo.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.pdf
===================================================================
(Binary files differ)

Index: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.pdf
===================================================================
--- branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.pdf	2025-02-27 20:15:24 UTC (rev 74327)
+++ branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.pdf	2025-02-27 20:15:53 UTC (rev 74328)

Property changes on: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.tex
===================================================================
--- branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.tex	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.tex	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,23 @@
+%%
+%% This is file `indextra-doc.tex',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% indextra.dtx  (with options: `metadriver')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2025 Alan J. Cain
+%% 
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%% 
+%% http://www.latex-project.org/lppl.txt
+%% 
+%% and version 1.3c or later is part of all distributions of
+%% LaTeX version 2008-05-04 or later.
+%% 
+\input{indextra.dtx}


Property changes on: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra-doc.tex
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra.xdy
===================================================================
--- branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra.xdy	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/doc/latex/indextra/indextra.xdy	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,62 @@
+;;
+;; This is file `indextra.xdy',
+;; generated with the docstrip utility.
+;;
+;; The original source files were:
+;;
+;; indextra.dtx  (with options: `xdy')
+;; 
+;; This is a generated file.
+;; 
+;; Copyright (C) 2025 Alan J. Cain
+;; 
+;; This file may be distributed and/or modified under the
+;; conditions of the LaTeX Project Public License, either
+;; version 1.3c of this license or (at your option) any later
+;; version. The latest version of this license is in:
+;; 
+;; http://www.latex-project.org/lppl.txt
+;; 
+;; and version 1.3c or later is part of all distributions of
+;; LaTeX version 2008-05-04 or later.
+;; 
+(define-attributes (("textit" "defterm")))
+
+(markup-index :open  "\begin{theindextra}~n  \indextrastyleversion{0.1}~n  \indextraprocessortype{xdy}~n"
+              :close "~n~n\end{theindextra}~n"
+              :tree)
+
+(markup-letter-group-list :sep "~n  \indextraspace~n")
+(markup-letter-group :open-head "~n  % "
+                     :close-head "~n"
+                     :group "default")
+(markup-letter-group :open-head "~n  \indextragroup{"
+                     :close-head "}~n~n")
+
+(markup-indexentry :open "  \indextraentry{0}{"
+                   :close "}%~n"
+                   :depth 0)
+(markup-indexentry :open "}%~n    \indextraentry{1}{"
+                   :close ""
+                   :depth 1)
+(markup-indexentry :open "}%~n      \indextraentry{2}{"
+                   :close ""
+                   :depth 2)
+
+(markup-keyword-list :open "" :close "}{" :sep ";")
+
+(markup-locclass-list :open "" :sep ", " :close "")
+(markup-locref-list :open ""  :sep ", " :close "")
+
+(markup-range :sep "--")
+(markup-locref :open "\textit{" :close "}" :attr "textit")
+(markup-locref :open "\defterm{"  :close "}" :attr "defterm")
+
+(markup-crossref-list :class "see"
+                      :open "\see{"
+                      :sep "}{}, \see{"
+                      :close "}{}")
+(markup-crossref-list :class "seealso"
+                      :open "\seealso{"
+                      :sep "}{}, \seealso{"
+                      :close "}{}")

Added: branches/branch2024.final/Master/texmf-dist/makeindex/indextra/indextra.ist
===================================================================
--- branches/branch2024.final/Master/texmf-dist/makeindex/indextra/indextra.ist	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/makeindex/indextra/indextra.ist	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,49 @@
+%%
+%% This is file `indextra.ist',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% indextra.dtx  (with options: `ist')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2025 Alan J. Cain
+%% 
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%% 
+%% http://www.latex-project.org/lppl.txt
+%% 
+%% and version 1.3c or later is part of all distributions of
+%% LaTeX version 2008-05-04 or later.
+%% 
+page_precedence "ArRn"
+
+preamble        "\\begin{theindextra}\n  \\indextrastyleversion{0.1}\n  \\indextraprocessortype{ist}\n"
+postamble       "\n\n\n\\end{theindextra}\n"
+
+group_skip      "\n\n\n  \\indextraspace"
+
+heading_prefix  "\n\n\n  \\indextragroup{"
+heading_suffix  "}\n"
+headings_flag   1
+
+item_0          "\n  \\indextraentry{0}{"
+item_1          "\n    \\indextraentry{1}{"
+item_01          "\n    \\indextraentry{1}{"
+item_x1          "}{}\n    \\indextraentry{1}{"
+item_2          "\n      \\indextraentry{2}{"
+item_12          "\n    \\indextraentry{2}{"
+item_x2          "}{}\n    \\indextraentry{2}{"
+
+delim_0         "}{"
+delim_1         "}{"
+delim_2         "}{"
+delim_t         "}"
+
+encap_prefix    "\\"
+encap_infix     "{"
+encap_suffix    "}"


Property changes on: branches/branch2024.final/Master/texmf-dist/makeindex/indextra/indextra.ist
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.dtx
===================================================================
--- branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.dtx	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.dtx	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,1924 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2025 Alan J. Cain
+%
+% This file may be distributed and/or modified under the conditions of the LaTeX Project Public License, either version
+% 1.3c of this license or (at your option) any later version. The latest version of this license is in:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% and version 1.3c or later is part of all distributions of LaTeX version 2008-05-04 or later.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\PassOptionsToPackage{inline}{enumitem}
+\documentclass{l3doc}
+
+
+\makeatletter
+\ExplSyntaxOn
+
+\cs_gset:Npn \l at subsection { \@dottedtocline{2}{2.5em}{2.8em} }  % #2 = 1.5em
+\cs_gset:Npn \l at subsubsection { \@dottedtocline{3}{5.3em}{3.2em} }  % #2 = 1.5em
+
+\ExplSyntaxOff
+\makeatother
+
+
+\usepackage{xcolor}
+
+\definecolor{linkcolor}{rgb}{0.0,0.4,0.7}
+\colorlet{citecolor}{linkcolor}
+\colorlet{urlcolor}{linkcolor}
+
+\hypersetup{
+  linkcolor=linkcolor,%
+  citecolor=citecolor,%
+  urlcolor=urlcolor,%
+}
+
+
+\newcommand*\fullref[2]{%
+  \hyperref[#2]{#1\penalty 200\ \ref*{#2}}%
+}
+
+
+\numberwithin{figure}{section}
+\numberwithin{table}{section}
+
+
+
+\usepackage{mathtools}
+\DeclarePairedDelimiter{\abs}{\lvert}{\rvert}
+\DeclarePairedDelimiter{\set}{\lbrace}{\rbrace}
+
+
+
+\newcommand*\key[1]{\texttt{#1}}
+\newcommand*\val[1]{\texttt{#1}}
+\newcommand*\keyval[2]{\texttt{#1=#2}}
+\newcommand*\ttdashdash{\texttt{-}\texttt{-}\hskip 0pt\relax}
+
+
+\NewDocumentCommand{\default}{ m }{(\textit{Default:} #1)}
+
+
+\newcommand*\mcode[1]{\texttt{#1}}
+
+
+\newcommand*\param[1]{\texttt{\##1}}
+
+
+\newcommand*\MakeIndex{\textit{MakeIndex}}
+\newcommand*\upmendex{\texttt{upmendex}}
+\newcommand*\xindy{\texttt{xindy}}
+
+
+\usepackage{siunitx}
+\sisetup{
+  mode=match,
+}
+\DeclareSIUnit\point{pt}
+\DeclareSIUnit\spoint{sp}
+
+
+\newcommand*\purl[1]{\textsc{url:}~\url{#1}}
+
+
+\usepackage{listings}
+
+
+
+\usepackage{indextra}
+
+
+
+\begin{document}
+\DocInput{indextra.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+%
+%
+% \GetFileInfo{indextra.sty}
+%
+% \makeatletter
+% \lstset{
+%   language=[LaTeX]TeX,
+%   columns=fullflexible,
+%   keepspaces=true,
+%   commentstyle=\slshape,
+%   basicstyle=\ttfamily\lst at ifdisplaystyle\small\fi,
+% }
+% \makeatother
+%
+%
+%
+% \title{^^A
+% \pkg{indextra} ^^A
+%   --- Enhanced index typesetting^^A
+%   \thanks{This file describes \fileversion, last revised \filedate.}^^A
+% }
+%
+% \author{^^A
+%  Alan J. Cain^^A
+% }
+%
+% \date{Released \filedate}
+%
+% \maketitle
+%
+%
+%
+% \begin{abstract}
+%   This package provides some enhanced features for typesetting indexes, notably: (1) Continuation text when entries or
+%   sub-entries continue from one page or column to the next. (2) An interface for accessing marks created from index
+%   entries, so that (for example) a running head can include the range of index entries that appears on the page.
+% \end{abstract}
+%
+%
+%
+% \tableofcontents
+%
+%
+%
+% \begin{documentation}
+%
+% \section{Introduction}
+%
+% By default, an index in a \LaTeX\ document is typeset in two columns and provides no indication if a page or column
+% break occurs inside an index entry or sub-entry. The package
+% \pkg{repeatindex}\footnote{\purl{https://ctan.org/pkg/repeatindex}} handles a page or column break that occurs between
+% sub-entries within the same entry by repeating the keyword of the main entry. But it does not repeat the keyword of a
+% sub-entry when a page or column break occurs within it. Nor does it repeat the keyword of an entry (at the top level)
+% % if the break occurs mid-way through a long list of pages or ranges for that entry directly.
+% In \emph{The \TeX book}, Knuth described a plain \TeX\ method to create continuation texts using the mark mechanism
+% \cite[pp.~261--3]{knuth_texbook}, which does not have these limitations. But there are still situations in which this
+% method can cause problems: for instance, if the keyword in the index entry spans two or more lines, it may break
+% across a page or column and the continuation text would appear in the middle of the keyword.
+%
+% The \pkg{indextra} package is intended to create suitable continuation texts for all of these possibilities. An
+% example is shown in \fullref{Figure}{fig:indextra-doc-demo}, where the list of locations within a sub-entry is too
+% long to fit in the left-hand column. At the top of the right-hand column, \pkg{indextra} has indicated that both the
+% main entry and the sub-entry have both been continued. If there had been a page break as well as a column break, the
+% same indicators would have been placed at the start of the left-hand column of the next page.
+%
+% \begin{figure}[t]
+%   \centering
+%   \includegraphics{indextra-doc-demo.pdf}
+%   \caption{An example of how \pkg{indextra} continues an entry and a sub-entry after a column or page break.}
+%   \label{fig:indextra-doc-demo}
+% \end{figure}
+%
+% \pkg{indextra} tries to break columns intelligently. If only one line of a multi-line index entry would fit in the
+% remaining space of the current column, the column break is inserted first, since there would be no saving in space
+% because the continutation text would occupy (at least) one line of the next column. Similarly, it will not break a
+% column before or during any cross-references in the index entry.
+%
+% \pkg{indextra} also supplies a system for retrieving index entry keywords as marks, for use in running heads to show
+% the range of index entries on the current page. The keywords of the first and last top-level entries on each page are
+% available as \cs{indextrafirstmark} and \cs{indextralastmark}.
+%
+% \medskip
+% \noindent\textit{Caveat:}~~Although \pkg{indextra} has been used successfully for the indexes of the author's
+% books,\footnote{Available on the Internet Archive under Creative Commons licences:\par
+% (1)~A.J. Cain, \textit{Form \& Number: A History of Mathematical Beauty}. Lisbon, 2024.
+% \purl{https://archive.org/details/cain_formandnumber_ebook_large}\par
+% (2)~G.H. Hardy, \textit{An Annotated Mathematician's Apology}. With annotations and commentary by A.J. Cain. Lisbon,
+% 2019. \purl{https://archive.org/details/hardy_annotated}} in its current state it should be regarded as
+% semi-experimental, requiring further development, and having many limitations and some incompatibilites (see
+% \fullref{Subsection}{subsec:limitations}).
+%
+%
+%
+% \section{Requirements}
+%
+% \pkg{indextra} does not depend on any other packages, but requires a recent \LaTeX\ kernel with \pkg{expl3} support.
+% (Any kernel version since 2020-02-02 should suffice.)
+%
+%
+%
+% \section{Installation}
+%
+% To install \pkg{indextra} manually, run \texttt{tex indextra.ins} and copy the file \texttt{indextra.sty} to somewhere
+% \LaTeX\ can find it, \texttt{indextra.ist} to somewhere \MakeIndex\ and/or \upmendex\ can find it, and
+% \texttt{indextra.xdy} to somewhere \xindy\ can find it. To build the documentation, first compile
+% \texttt{indextra-doc-demo.tex} to a PDF, then compile \texttt{indextra-doc.tex}.
+%
+%
+%
+% \section{Getting started}
+%
+% \subsection{\LaTeX}
+%
+% On the \LaTeX\ side, simply use the package with \lstinline!\usepackage{indextra}!. (There are no package options.)
+% Use \cs{makeindex} and \cs{index} as as usual. Note that \pkg{indextra} is incompatible with \pkg{imakeindex}.
+%
+% If the \pkg{hyperref} package is used, set \keyval{hyperindex}{false} in its package options or in \cs{hypersetup},
+% because \pkg{indextra} has its own mechanism to add index hyperlinks, activated by its own \key{hyperindex} key.
+%
+%
+%
+% \subsection{Index generation}
+%
+% The \file{.ind} file (generated by \MakeIndex, \upmendex, or \xindy) must be specially formatted for \pkg{indextra}.
+% The style file \file{indextra.ist} is supplied for use with \MakeIndex\ and \upmendex. It can be used with:\par
+% \texttt{makeindex -s indextra.ist \meta{filename}.idx}\par
+% \noindent or\par
+% \texttt{upmendex -s indextra.ist \meta{filename}.idx}\par
+% \noindent Also supplied is a barebones \xindy\ module \file{indextra.xdy} style file, which should be used with other
+% modules to set up sorting and attributes. The module \file{indextra.xdy} should be specified last when invoking
+% \xindy, to override any previous specification of how to write to the \file{.idx} file. For example, one might
+% use:\par
+% \texttt{xindy -M makeidx.xdy -M utf8.xdy -M indextra.xdy \meta{filename}.idx}
+%
+%
+%
+% \section{Configuration}
+%
+% \begin{function}{\indextrasetup}
+%   \begin{syntax}
+%     \cs{indextrasetup}\marg{options}
+%   \end{syntax}
+%   This command is used to specify options. The argument \meta{options} is a key--value list. The options are described
+%   in the subsections below and are summarized in \fullref{Table}{tbl:keys-summary}.
+% \end{function}
+%
+% \begin{table}[t]
+%   \addtolength{\leftskip}{-12mm}%
+%   \addtolength{\textwidth}{12mm}%
+%   \caption{Summary of keys that can be set using \cs{indextrasetup}.}
+%   \label{tbl:keys-summary}
+%   \begin{tabular*}{\textwidth}{lll@{\extracolsep{\fill}}}
+%     \toprule
+%     \textsf{Key name}                 & \textsf{Value}                   & \textsf{Default}                                 \\
+%     \midrule
+%     \key{before code}                 & \LaTeX\ code                     & \val{\cs{begin}\{theindex\}}                     \\
+%     \key{after code}                  & \LaTeX\ code                     & \val{\cs{end}\{theindex\}}                       \\
+%     \key{level 0 style}               & \LaTeX\ code                     & \val{\cs{parindent}=0em\cs{hangindent}=3.75em}   \\
+%     \key{level 1 style}               & \LaTeX\ code                     & \val{\cs{parindent}=1.5em\cs{hangindent}=5.25em} \\
+%     \key{level 2 style}               & \LaTeX\ code                     & \val{\cs{parindent}=3em\cs{hangindent}=6.75em}   \\
+%     \key{separator keyword crossref}  & \LaTeX\ code                     & \cs{break}                                       \\
+%     \key{separator keyword location}  & \LaTeX\ code                     & \cs{space}\cs{space}                             \\
+%     \key{separator crossref crossref} & \LaTeX\ code                     & \cs{break}                                       \\
+%     \key{separator crossref location} & \LaTeX\ code                     & \cs{break}                                       \\
+%     \key{separator location location} & \LaTeX\ code                     & \val{,\textvisiblespace}                         \\
+%     \key{crossref macros}             & List of macros                   & \cs{see}\cs{seealso}                             \\
+%     \key{hyperindex}                  & \(\set{\val{true},\val{false}}\) & \val{true}                                       \\
+%     \key{bookmarks}                   & \(\set{\val{true},\val{false}}\) & \val{true}                                       \\
+%     \bottomrule
+%   \end{tabular*}
+% \end{table}
+%
+%
+%
+% \subsection{Before and after code}
+%
+% \DescribeOption{before code}
+% This option specifies code that is executed at the start of the index (more specifically,
+% at the start of the \env{theindextra} environment in the generated \file{.ind} file). The code will be executed before
+% \pkg{indextra} sets various parameters and makes available the commands described in
+% \fullref{Section}{sec:ind-macros}. The user may wish to use this option to set up running heads using \pkg{indextra}'s
+% marks (see \fullref{Subsection}{subsec:example-marks} for an example).  \default{\cs{begin}\mcode{\{theindex\}}}
+%
+% \DescribeOption{after code}
+% This option specifies code that is executed at end start of the index (more specifically,
+% at the end of the \env{theindextra} environment in the generated \file{.ind} file).
+% \default{\cs{begin}\mcode{\{theindex\}}}
+%
+%
+%
+% \subsection{Styles}
+%
+% \DescribeOption{level \(n\) style} The style applied to an index entry at level \(n = 0,1,2\) (that is, respectively
+% to an entry, a sub-entry, and a sub-sub-entry).
+%
+% \noindent
+% \begin{tabular}{@{}r@{~}l}
+%   (\textit{Default:}                  & \keyval{level 0 style}{\cs{parindent}=0em\allowbreak\cs{hangindent}=3.75em};        \\
+%                                       & \keyval{level 1 style}{\cs{parindent}=1.5em\cs{hangindent}=5.25em};                 \\
+%                                       & \keyval{level 2 style}{\cs{parindent}=3em\allowbreak\cs{hangindent}=6.75em})
+% \end{tabular}
+%
+%
+%
+% \subsection{Separators}
+%
+% \pkg{indextra} always typesets cross-references before locations in each index entry, sub-entry, or sub-sub-entry. The
+% following options specify the separators to be used.
+%
+% \DescribeOption{separator keyword crossref}
+% The separator to be placed between the keyword of an index entry and a cross-reference. \default{\cs{break}}
+%
+% \DescribeOption{separator keyword location}
+% The separator to be placed between the keyword of an index entry and a location (that is, a page or range of pages),
+% when the first location immediately follows the keyword (when there are no cross-references).
+% \default{\cs{space}\cs{space}}
+%
+% \DescribeOption{separator crossref crossref}
+% The separator to be placed between the two cross-references in an index entry. \default{\cs{break}}
+%
+% \DescribeOption{separator crossref location}
+% The separator to be placed between the last cross-reference in an entry and the first location. \default{\cs{break}}
+%
+% \DescribeOption{separator location location}
+% The separator to be placed between two locations in an index entry. \default{\texttt{,\textvisiblespace}}
+%
+%
+%
+% \subsection{Specification of cross-references}
+%
+% \DescribeOption{crossref macros}
+% A list of macros that signify cross-references. \default{\cs{see}\cs{seealso}}
+%
+%
+%
+% \subsection{Hyperlinks}
+%
+% \DescribeOption{hyperindex}
+% A boolean \val{true}/\val{false} that indicates whether index locations should be hyperlinked if the \cs{hyperpage}
+% macro from the \pkg{hyperref} package is available. Note that a user redefinition of \cs{indextrapage} or
+% \cs{indextrarange} (see \fullref{Subsection}{subsec:user-redefinable-page-range}) will override this setting. If
+% hyperlinks are required in this case, the user's redefinition should create them. \default{\val{true}}
+%
+%
+%
+% \subsection{Bookmarks}
+%
+% \DescribeOption{bookmarks}
+% A boolean \val{true}/\val{false} that indicates whether the beginning of each group of index entries (usually terms
+% beginning with a particular letter) should be bookmarked if the \cs{belowpdfbookmark} macro from the \pkg{hyperref}
+% package is available. \default{\val{true}}
+%
+%
+%
+% \subsection{Headings}
+%
+% \DescribeOption{headings} A boolean \val{true}/\val{false} that indicates whether there should be a heading at the
+% beginning of each group of index entries (usually terms beginning with a particular letter). This heading will be
+% created by the user-redefinable macro \cs{indextramakegroupheading} (see
+% \fullref{Subsection}{subsec:user-redefinable-heading}). \default{\val{true}}
+%
+%
+%
+% \section{Marks}
+%
+% \pkg{indextra} uses the keyword of each (top-level) index entry to insert a mark. The following macros make available
+% the first and last marks on the current page. They are intended for use in a running header to show the range of
+% entries on the current page.
+%
+% \begin{function}{\indextrafirstmark}
+%   The first mark on the current page, with the macro \cs{indextramakemark} applied.
+% \end{function}
+%
+% \begin{function}{\indextralastmark}
+%   The last mark on the current page, with the macro \cs{indextramakemark} applied.
+% \end{function}
+%
+%
+%
+% \section{User-redefinable macros}
+%
+% \subsection{Keywords}
+%
+% The following macros is used to typeset keywords of entries, sub-entries, and sub-sub-entries. It can be redefined by
+% the user.
+%
+% \begin{function}{\indextrakeyword}
+%   \begin{syntax}
+%     \cs{indextrakeyword}\marg{level}\marg{keyword}
+%   \end{syntax}
+%   This macro is used to typeset a keyword for an entry of level \meta{level}. The default definition simply yields
+%   \meta{keyword}. Users may wish to use a redefinition to apply styling or for other purposes. The result will already
+%   have the code specified in \key{level \meta{level} style} applied.
+% \end{function}
+%
+%
+%
+% \subsection{Page and range typesetting}
+% \label{subsec:user-redefinable-page-range}
+%
+% The following two macros are actually used to typeset locations. They can be redefined by the user.
+%
+% \begin{function}{\indextrapage}
+%   \begin{syntax}
+%     \cs{indextrapage}\marg{encapsulation}\marg{page}
+%   \end{syntax}
+%   This command is used to typeset a reference to a single page. The default definition is effectively
+%   \meta{encapsulation}\texttt{\{}\meta{page}\texttt{\}}, but using the configuration option \keyval{hyperindex}{true}
+%   will create a hyperlink to the actual page. Any redefinition of this command will override the effect of
+%   \keyval{hyperindex}{true}, so if the user still wishes the reference to be a hyperlink to the actual page, the new
+%   definition must create the hyperlink.
+% \end{function}
+%
+% \begin{function}{\indextrarange}
+%   \begin{syntax}
+%     \cs{indextrarange}\marg{encapsulation}\marg{start}\marg{end}
+%   \end{syntax}
+%   This command is used to typeset a reference to a range of pages. The default definition is effectively
+%   \meta{encapsulation}\texttt{\{}\meta{page}\texttt{\}}\ttdashdash\meta{encapsulation}\texttt{\{}\meta{page}\texttt{\}},
+%   but using the configuration option \keyval{hyperindex}{true} will create a hyperlink to the actual pages. Users may
+%   wish to redefine \cs{indextrarange} to abbreviate ranges (so that, for instance, 1024--1025 is replaced by 1024--5).
+%   Any redefinition of this command will override the effect of \keyval{hyperindex}{true}, so if the user still wishes
+%   the references to be hyperlinks to the actual pages, the new definition must create the hyperlinks.
+% \end{function}
+%
+%
+%
+% \subsection{Continuation text}
+%
+% \pkg{indextra} inserts continuation text for each entry, sub-entry, and sub-sub-entry that has been interrupted by a
+% page or column break. The following macro creates the continuation text and can be redefined by the user.
+%
+% \begin{function}{\indextramakecontinuation}
+%   \begin{syntax}
+%     \cs{indextramakecontinuation}\marg{level}\marg{keyword}
+%   \end{syntax}
+%   This macro is used to generate a continuation text when the entry at \meta{level} with the supplied \meta{keyword}
+%   contains a column break. The default definition simply yields \meta{keyword}\texttt{ (cont.)}. Users may wish to use
+%   a redefinition to apply styling. The continuation text will already have the code specified in \key{level
+%   \meta{level} style} applied.
+% \end{function}
+%
+%
+%
+% \subsection{Marks}
+%
+% \begin{function}{\indextramakemark}
+%   \begin{syntax}
+%     \cs{indextramakemark}\marg{text}
+%   \end{syntax}
+%   This command is applied to any mark retrieved via either \cs{indextrafirstmark} or \cs{indextralastmark}. The
+%   default definition simply yields \meta{text}. A redefinition could be used to abbreviate or otherwise process
+%   \meta{text}.
+% \end{function}
+%
+%
+%
+% \subsection{Headings}
+% \label{subsec:user-redefinable-heading}
+%
+% If the option \keyval{headings}{true} is set, the following macro generates the group heading and can be
+% redefined by the user.
+%
+% \begin{function}{\indextramakegroupheading}
+%   \begin{syntax}
+%     \cs{indextramakegroupheading}\marg{group}
+%   \end{syntax}
+%   This macro is used to generate a heading for \meta{group}. The default definition yields \cs{textbf}\marg{group}.
+%   Users may wish to use a redefinition to apply a different style. Note that even if this command is redefined to
+%   yield nothing, an extra vertical space will still be produced in the index where the heading would have been. To
+%   disable headings and avoid this extra space, set \keyval{headings}{false} using \cs{indextrasetup}.
+% \end{function}
+%
+%
+%
+% \section{Macros used in the generated \texorpdfstring{\file{.ind}}{.ind} file}
+% \label{sec:ind-macros}
+%
+% For completeness, this section documents the commands and the enclosing environment used in the generated \file{.ind}
+% file. None of these is intended to be used or redefined by the user.
+%
+% \begin{environment}{theindextra}
+%   Environment containing the generated index.
+% \end{environment}
+%
+% The commands below are only defined inside the \env{theindextra} environment.
+%
+% \begin{function}{\indextrastyleversion}
+%   \begin{syntax}
+%     \cs{indextrastyleversion}\marg{version}
+%   \end{syntax}
+%   Specify the \pkg{indextra} version of the style used to generate the \file{.ind} file. (This macro exists in case
+%   future updates change the required format of the \file{.ind} file.)
+% \end{function}
+%
+% \begin{function}{\indextraprocessortype}
+%   \begin{syntax}
+%     \cs{indextraprocessortype}\marg{type}
+%   \end{syntax}
+%   Specify the type of the processor used to generate the \file{.ind} file. (Different encapsulations of ranges, which
+%   must be handled differently, are generated by \MakeIndex/\upmendex\ and by \xindy.) \marg{type} is either
+%   \mcode{ist}, indicating that \MakeIndex\ or \upmendex\ was used, or \mcode{xdy}, indicating that \xindy\ was used.
+% \end{function}
+%
+% \begin{function}{\indextraentry}
+%   \begin{syntax}
+%     \cs{indextraentry}\marg{level}\marg{keyword}\marg{crossrefs-and-locations}
+%   \end{syntax}
+%   This command typesets an index entry, sub-entry, or sub-sub-entry (if \meta{level} is respectively \mcode{0},
+%   \mcode{1}, or \mcode{2}) with the given \meta{keyword}. The parameter \meta{crossrefs-and-locations} is a
+%   comma-separated list of cross-references (using macros such as \cs{see} or \cs{seealso} as specified in the
+%   \key{crossref macros} option) and locations (meaning pages or ranges).
+% \end{function}
+%
+% \begin{function}{\indextraspace}
+%   \begin{syntax}
+%     \cs{indextraspace}
+%   \end{syntax}
+%   This command produces a space of one line in the index.
+% \end{function}
+%
+% \begin{function}{\indextragroup}
+%   \begin{syntax}
+%     \cs{indextragroup}\marg{letter}
+%   \end{syntax}
+%   This command begins the new group \meta{letter}. Depending on configuration, it may produce a heading and/or a
+%   bookmark.
+% \end{function}
+%
+%
+%
+% \section{Usage notes and caveats}
+% \label{sec:usage-notes}
+%
+% \subsection{Typesetting process}
+%
+% \pkg{indextra} works by tracking how much space is left in each column and intially typesetting each index entry into
+% a buffer and measuring it. If there is enough space left in the column, the buffer contents are added to the output
+% and the amount of space left is adjusted appropriately. Otherwise, enough of the buffer as will fit is split off and
+% output, then a column break is called, the continuation text is added to the top of the remaining content in the
+% buffer, and the process continues in a new column.
+%
+% This process relies on the fact that an index is a highly structured text with a restricted format. Any `exotic' index
+% entries may break the process.
+%
+%
+%
+% \subsection{Limitations and incompatibilies}
+% \label{subsec:limitations}
+%
+% \begin{itemize}
+%
+%     \item When choosing where to break a column, \pkg{indextra} only considers the current entry at the current level.
+%           It does not, for example, automatically decide to insert a column break before a one-line top-level entry
+%           that contains a sub-entry, even though this would be a more desirable result.
+%
+%     \item \pkg{indextra} cannot cope with \cs{verb} commands in index keywords. (But succeeding in generating such an
+%           \file{.ind} via the usual \LaTeX\ indexing commands would be a challenge.)
+%
+%     \item \pkg{indextra} is incompatible with \pkg{imakeidx}, which uses the \pkg{multicols} package to set the index
+%           instead of the native \LaTeX\ two-column mode. In particular, \LaTeX\ marks cannot be set from within
+%           the \env{multicols} environment.
+%
+%     \item \pkg{indextra} should not be used with \pkg{repeatindex}, and in any case replaces its functionality.
+%
+%     \item \pkg{indextra} cannot be used if the usual indexing system is heavily customized. For example, the
+%           \cls{l3doc} class uses its own specialized implementation of indexing and so \pkg{indextra} cannot be used
+%           alongside it.
+%
+% \end{itemize}
+%
+%
+%
+% \subsection{Example of using marks}
+% \label{subsec:example-marks}
+%
+% One could incorporate marks into the index running heads using by appending suitable code to the default value
+% (\cs{begin}\mcode{\{theindex\}}) of the \key{before code} key:
+% \iffalse
+%<*example>
+% \fi
+\begin{lstlisting}
+\indextrasetup{
+  before code={%
+    \begin{theindex}%
+      \markboth
+      {\MakeUppercase\indexname~%
+        \noexpand\indextrafirstmark--\noexpand\indextralastmark}
+      {\MakeUppercase\indexname~%
+        \noexpand\indextrafirstmark--\noexpand\indextralastmark}%
+  },
+}
+\end{lstlisting}
+% \iffalse
+%</example>
+% \fi
+% (The \cs{noexpand} macros ensure that \cs{indextrafirstmark} and \cs{indextralastmark} are expanded when
+% the page is shipped out, not when \cs{markboth} is used.)
+%
+%
+%
+% \begin{thebibliography}{1}
+%
+% \bibitem{knuth_texbook}
+% D.E.~Knuth.
+% \newblock \emph{{T}he {\TeX book}}.
+% \newblock Addison--Wesley, 2021.
+% \newblock \emph{{C}omputers {\&} {T}ypesetting}, vol.~A.
+%
+% \end{thebibliography}
+%
+%
+%
+% \end{documentation}
+%
+%
+%
+% \begin{implementation}
+%
+%
+%
+% \section{Implementation}
+%
+%    \begin{macrocode}
+%<*package>
+%<@@=indextra>
+%    \end{macrocode}
+%
+%
+%
+% \subsection{Initial set-up}
+%
+% Package identification/version information.
+%    \begin{macrocode}
+\NeedsTeXFormat{LaTeX2e}[2020-02-02]
+\ProvidesExplPackage{indextra}{2025-02-26}{0.21.2}
+  {Enhanced index typesetting}
+%    \end{macrocode}
+%
+%
+%
+% \subsection{User configuration}
+%
+% Set up the key--value options and the variables in which the settings will be stored.
+%
+% \begin{variable}{
+%   \l_@@_before_code_tl,
+%   \l_@@_after_code_tl,
+% }
+%   Token list keys that store code to be executed before and after the index is typeset.
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  before~code .tl_set:N = \l_@@_before_code_tl,
+  after~code .tl_set:N = \l_@@_after_code_tl,
+  before~code .initial:n = {\begin{theindex}},
+  after~code .initial:n = {\end{theindex}},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_level_0_style_tl,
+%   \l_@@_level_1_style_tl,
+%   \l_@@_level_2_style_tl,
+% }
+%   Token list keys that store the style to be applied to entries at each level.
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  level~0~style .tl_set:c = {l_@@_level_0_style_tl},
+  level~1~style .tl_set:c = {l_@@_level_1_style_tl},
+  level~2~style .tl_set:c = {l_@@_level_2_style_tl},
+  level~0~style .initial:n = {\parindent=0em\hangindent=3.75em},
+  level~1~style .initial:n = {\parindent=1.5em\hangindent=5.25em},
+  level~2~style .initial:n = {\parindent=3em\hangindent=6.75em},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_separator_kw_cr_tl,
+%   \l_@@_separator_kw_loc_tl,
+%   \l_@@_separator_cr_cr_tl,
+%   \l_@@_separator_cr_loc_tl,
+%   \l_@@_separator_loc_loc_tl,
+% }
+%   Token list keys to store separators. Macros are abbreviated as follows \mcode{kw}: keyword; \mcode{cr}:
+%   cross-reference; \mcode{loc}: location (page or range).
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  separator~keyword~crossref .tl_set:N = \l_@@_separator_kw_cr_tl,
+  separator~keyword~location .tl_set:N = \l_@@_separator_kw_loc_tl,
+  separator~crossref~crossref .tl_set:N = \l_@@_separator_cr_cr_tl,
+  separator~crossref~location .tl_set:N = \l_@@_separator_cr_loc_tl,
+  separator~location~location .tl_set:N = \l_@@_separator_loc_loc_tl,
+  separator~keyword~crossref .initial:n = {\break},
+  separator~keyword~location .initial:n = {\space\space},
+  separator~crossref~crossref .initial:n = {\break},
+  separator~crossref~location .initial:n = {\break},
+  separator~location~location .initial:n = {,~},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_crossref_macros_tl,
+% }
+%   Token list key to store macros that should count as cross-references.
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  crossref~macros .tl_set:N = \l_@@_crossref_macros_tl,
+  crossref~macros .initial:n = {\see\seealso},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_hyperindex_bool,
+% }
+%   Boolean indicating whether locations should be hyperlinked to pages, if \cs{hyperpage} is available and the user has
+%   not redefined \cs{indextrapage} and \cs{indextrarange}.
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  hyperindex .bool_set:N = \l_@@_hyperindex_bool,
+  hyperindex .initial:n = {true},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_bookmarks_bool,
+% }
+%   Boolean indicating whether groups should be bookmarked, if \cs{belowpdfbookmark} is available.
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  bookmarks .bool_set:N = \l_@@_bookmarks_bool,
+  bookmarks .initial:n = {true},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_headings_bool,
+% }
+%   Boolean indicating whether group headings should appear.
+%    \begin{macrocode}
+\keys_define:nn { indextra }
+{
+  headings .bool_set:N = \l_@@_headings_bool,
+  headings .initial:n = {true},
+}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{macro}{
+%   \indextrasetup,
+% }
+%   User command to set key--value configuration.
+%    \begin{macrocode}
+\NewDocumentCommand{\indextrasetup}{ m }
+{
+  \keys_set:nn{ indextra }{ #1 }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsection{Environment and commands for the \texorpdfstring{\file{.ind}}{.ind} file}
+%
+% Define the environment and commands used in the generated \file{.ind} file.
+%
+% \begin{macro}{
+%   theindextra
+% }
+%   This environment is the analogue of the \env{theindex} environment defined by basic \LaTeX.
+%    \begin{macrocode}
+\NewDocumentEnvironment{theindextra}{}
+  { \@@_main_begin: }
+  { \@@_main_end: }
+%
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_main_begin:
+% }
+%   This macro (locally) sets various \TeX\ dimensions and defines the commands that are actually used in the index,
+%   namely \cs{indextrastyleversion}, \cs{indextraprocessortype}, \cs{indextraspace}, \cs{indextragroup}, and
+%   \cs{indextraentry}.%
+%    \begin{macrocode}
+\cs_new:Npn \@@_main_begin:
+{
+%    \end{macrocode}
+%   Open a group and execute code for before the index has been typeset.
+%    \begin{macrocode}
+  \group_begin:
+  \tl_use:N\l_@@_before_code_tl
+%    \end{macrocode}
+%   Set \cs{splittopskip}, \cs{topskip}, and \cs{parskip} to the needed values, and set
+%   \cs{g_@@_remaining_space_dim}, which tracks the space left in the current column, to the initial value of
+%   \cs{@colht}, which will be the height of the column after any initial two-column text has been typeset.
+%    \begin{macrocode}
+  \dim_gset:Nn\g_@@_remaining_space_dim{\@colht}
+  \dim_set:Nn\splittopskip{.7\baselineskip}
+  \dim_set:Nn\topskip{.7\baselineskip}
+  \dim_set:Nn\parskip{0pt}
+%    \end{macrocode}
+%   Make available the appropriate macros.
+%    \begin{macrocode}
+  \cs_set_eq:NN\indextrastyleversion\@@_style_version:n
+  \cs_set_eq:NN\indextraprocessortype\@@_processor_type:n
+  \cs_set_eq:NN\indextraspace\@@_space:
+  \cs_set_eq:NN\indextragroup\@@_group:n
+  \cs_set_eq:NN\indextraentry\@@_entry:nnn
+%    \end{macrocode}
+%   If user settings require it, or \cs{belowpdfbookmark} is unavailable, disable group bookmarks and/or headings.
+%    \begin{macrocode}
+  \bool_if:nF
+    { \l_@@_bookmarks_bool && \cs_if_exist_p:N\belowpdfbookmark }
+    {
+      \cs_set_eq:NN\@@_bookmark_stored_group:\prg_do_nothing:
+    }
+  \bool_if:NF\l_@@_headings_bool
+    {
+      \cs_set_eq:NN\@@_typeset_stored_group_heading:\prg_do_nothing:
+    }
+%    \end{macrocode}
+%   Depending on user configuration, if \cs{hyperpage} is available and \cs{indextrapage} or \cs{indextrapage} have not
+%   been redefined, then change them to create hyperlinks.
+%    \begin{macrocode}
+  \bool_if:nT{ \l_@@_hyperindex_bool && \cs_if_exist_p:N\hyperpage }
+    {
+      \cs_if_eq:NNT\indextrapage\@@_page_basic:nn
+        {
+          \cs_set_eq:NN\indextrapage\@@_page_hyperref:nn
+        }
+      \cs_if_eq:NNT\indextrarange\@@_range_basic:nnn
+        {
+          \cs_set_eq:NN\indextrarange\@@_range_hyperref:nnn
+        }
+    }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_main_end:
+% }
+%   Execute code for after the index has been typeset and close the group opened in \cs{@@_main_begin:}.
+%    \begin{macrocode}
+\cs_new:Npn \@@_main_end:
+{
+  \tl_use:N\l_@@_after_code_tl
+  \group_end:
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+\msg_new:nnn{ indextra }{ incompatible_version }
+  { The~.ind~file~was~generated~using~a~style~from~a~different~version~of~indextra. }
+%    \end{macrocode}%
+%
+% \begin{macro}{
+%   \@@_style_version:n,
+% }
+%   Specify the version of \pkg{indextra}'s index processor style file (\file{.ist} or \file{.xdy}) that was used to
+%   generate the \file{.ind} file. If is incompatible with version of \pkg{indextra} in use, an error will result.
+%    \begin{macrocode}
+\cs_new:Npn\@@_style_version:n #1
+{
+  \str_if_eq:nnF{#1}{0.1}
+    {
+      \msg_error:nn{ indextra }{ incompatible_version }
+    }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{
+%   \@@_processor_type:n,
+% }
+%   Set the type of the processor used to generate the \file{.ind} file. \MakeIndex\ and \upmendex\ produce ranges of
+%   the form \cs{encapsulation}\texttt{\{}\meta{start}\ttdashdash\meta{end}\texttt{\}}, while \xindy\ produces ranges of
+%   the form \cs{encapsulation}\texttt{\{}\texttt{\}}\ttdashdash\cs{encapsulation}\texttt{\{}\texttt{\}}. These kinds of
+%   ranges must be parsed differently, and while the kind of range could be detected when typesetting each location,
+%   there are efficiency savings from using a dedicated parser in each case. \param{1} should be either \mcode{ist},
+%   indicating that the processor was either \MakeIndex\ or \upmendex, or \mcode{xdy}, that the processor was \xindy.
+%   This macro will be set equal to \cs{indextrasetprocessortype} inside the \env{theindextra} environment.
+%    \begin{macrocode}
+\cs_new:Npn\@@_processor_type:n #1
+{
+  \cs_set_eq:Nc\@@_typeset_aux_location:n
+    { @@_typeset_#1_location:n }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+% \subsection{Column handling}
+%
+% \begin{variable}{
+%   \g_@@_remaining_space_dim,
+% }
+%   Dimension variable to hold the amount of space left in the current column.
+%    \begin{macrocode}
+\dim_new:N\g_@@_remaining_space_dim
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{
+%   \@@_new_column:
+% }
+%   Fill any remaining space in the current column, start a new column, and set \cs{g_@@_remaining_space_dim} to
+%   \cs{@colht} (which is the amount of space available in the new column).
+%    \begin{macrocode}
+\cs_new:Npn\@@_new_column:
+  {
+    \vfill
+    \newpage
+    \dim_gset:Nn\g_@@_remaining_space_dim{\@colht}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsection{Spaces}
+%
+% \begin{macro}{
+%   \@@_space:,
+% }
+%   Create a space in the index, which will usually signify the end of one letter group and the beginning of another.
+%   This takes up space in the column, so the space (a \cs{strut}) is typeset via the same mechanism as index entries,
+%   namely \cs{@@_typeset_buffer:}.
+%    \begin{macrocode}
+\cs_new:Npn\@@_space:
+  {
+    \dim_compare:nNnT{\g_@@_remaining_space_dim}<{\baselineskip}{
+      \@@_new_column:
+    }
+    \vbox_set:Nn\l_@@_buffer_box{\leavevmode\strut\par}
+    \@@_typeset_buffer:
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsection{Groups}
+%
+% \begin{variable}{
+%   \l_@@_group_tl
+% }
+%   Token-list variable to store the current group.
+%    \begin{macrocode}
+\tl_new:N\l_@@_group_tl
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{macro}{
+%   \@@_group:,
+% }
+%   Begin a letter group in the index. The macro simply stores the group title; the bookmark and/or heading will be
+%   created by the next index entry. This macro will be set equal to \cs{indextragroup} inside the \env{theindextra}
+%   environment.
+%    \begin{macrocode}
+\cs_new:Npn\@@_group:n #1
+  {
+    \tl_set:Nn\l_@@_group_tl{#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{variable}{
+%   \g_@@_group_bookmark_int
+% }
+%   Integer variable to index groups; used in the creation of bookmarks in \cs{@@_bookmark_stored_group:}.
+%    \begin{macrocode}
+\int_new:N\g_@@_group_bookmark_int
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{macro}{
+%   \@@_bookmark_stored_group:
+% }
+%   If \cs{l_@@_group_tl} is non-empty, create a bookmark below the current level (i.e., using \cs{belowpdfbookmark}),
+%   using \cs{l_@@_group_tl} for the bookmark text. The variable \cs{g_@@_group_bookmark_int} is incremented at each
+%   call and used to create distinct bookmark names. (One cannot use the the content of \cs{l_@@_group_tl} for the
+%   bookmark name, because (1) it might not be suitable as a name, and (2) there may be two indexes (e.g. names and
+%   subjects) which contain the same groups.)
+%    \begin{macrocode}
+\cs_new:Npn\@@_bookmark_stored_group:
+  {
+    \tl_if_empty:NF\l_@@_group_tl
+      {
+        \int_gincr:N\g_@@_group_bookmark_int
+        \belowpdfbookmark
+          {\tl_use:N\l_@@_group_tl}
+          {pdf:indextra:\int_value:w\g_@@_group_bookmark_int}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{
+%   \@@_typeset_stored_group_heading:
+% }
+%   If \cs{l_@@_group_tl} is non-empty, typeset its contents as a heading for the group.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_stored_group_heading:
+  {
+    \tl_if_empty:NF\l_@@_group_tl
+      {
+        \group_begin:
+        \noindent\strut
+        \indextramakegroupheading{\tl_use:N\l_@@_group_tl}
+        \strut\par
+        \group_end:
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{
+%   \indextramakegroupheading
+% }
+%   Typeset a heading for the letter group passed as \param{1}.
+%    \begin{macrocode}
+\cs_new:Npn\indextramakegroupheading #1
+  {
+    \textbf{#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+%
+% \subsection{Entries}
+%
+% \begin{variable}{
+%   \l_@@_level_int
+% }
+%   Integer variable to hold the level of the current entry.
+%    \begin{macrocode}
+\int_new:N\l_@@_level_int
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_saved_keyword_0,
+%   \l_@@_saved_keyword_1,
+%   \l_@@_saved_keyword_2,
+% }
+%   Token list variables to save keywords at each level.
+%    \begin{macrocode}
+\tl_new:c{l_@@_saved_keyword_0}
+\tl_new:c{l_@@_saved_keyword_1}
+\tl_new:c{l_@@_saved_keyword_2}
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{variable}{
+%   \l_@@_crossref_seq,
+%   \l_@@_location_seq,
+% }
+%   Comma-separated list variables to hold the cross-references and locations of the current entry. These will be
+%   populated via a call to \cs{@@_filter_crossref_location_seqs:n}
+%    \begin{macrocode}
+\seq_new:N\l_@@_crossref_seq
+\seq_new:N\l_@@_location_seq
+%    \end{macrocode}
+% \end{variable}
+%
+%
+% \begin{macro}{
+%   \@@_entry:nnn,
+% }
+%   Typeset an index entry. The three parameters are the level (\param{1}), the keyword (\param{2}), and the rest of the
+%   entry (\param{3}), which should be a comma-separated list of cross-references and/or locations.
+%    \begin{macrocode}
+\cs_new:Npn\@@_entry:nnn #1#2#3
+  {
+%    \end{macrocode}
+%   First save the level and the keyword, and sort the rest of the entry into the cross-reference and location seqs.
+%    \begin{macrocode}
+    \int_set:Nn\l_@@_level_int{#1}
+    \tl_set:cn{l_@@_saved_keyword_#1}{#2}
+    \@@_filter_crossref_location_seqs:n{#3}
+%    \end{macrocode}
+%   Typeset the entire entry (prefixed by any stored group heading) into \cs{l_@@_buffer_box}. The bookmarking command
+%   is also executed here, since it is the contents of this box that will ultimately be shipped out.
+%    \begin{macrocode}
+    \vbox_set:Nn\l_@@_buffer_box{
+      \@@_bookmark_stored_group:
+      \@@_typeset_stored_group_heading:
+      \group_begin:
+      \@@_set_style:n{#1}
+      \leavevmode
+      \strut
+      \indextrakeyword{#1}{#2}
+      \@@_typeset_between:
+      \@@_typeset_locations:
+      \strut
+      \par
+      \goodbreak
+      \group_end:
+    }
+%    \end{macrocode}
+%   Now typeset some material into \cs{l_tmpa_box} and \cs{l_tmpb_box}, the dimensions of which will be used in
+%   computing the minimum acceptable portion of the entry to typeset into the current column. First, typeset the part of
+%   the entry where a column break is unacceptable (prefixed by any stored group heading) into \cs{l_tmpa_box}.
+%    \begin{macrocode}
+    \vbox_set:Nn\l_tmpa_box{
+      \@@_typeset_stored_group_heading:
+      \group_begin:
+      \@@_set_style:n{#1}
+      \leavevmode
+      \strut
+      \indextrakeyword{#1}{#2}
+      \@@_typeset_between:
+      \@@_typeset_first_location:
+      \strut
+      \par
+      \group_end:
+    }
+%    \end{macrocode}
+%   Typeset any stored group heading into \cs{l_tmpb_box}.
+%    \begin{macrocode}
+    \vbox_set:Nn\l_tmpb_box{
+      \@@_typeset_stored_group_heading:
+    }
+%    \end{macrocode}
+%   Measuring \cs{l_tmpa_box}, \cs{l_tmpb_box}, and \cs{l_@@_buffer_box}, check whether there is enough space in the
+%   current column to typeset any stored heading and the minimum acceptable portion of the entry. Basically, a split
+%   should not occur within the part typeset into \cs{l_tmpa_box}, and if the entry as a whole is longer than one line,
+%   then at least two lines should be typeset (because if only one line can fit into the current column, there is
+%   probably no overall saving of space by putting it in the current column with a continuation in the next column).
+%   Note that if there is no stored heading, the height and depth of \cs{l_tmpb_box} are both \qty{0}{\point}, so no
+%   conditional is needed.
+%    \begin{macrocode}
+    \dim_compare:nNnT
+      {\g_@@_remaining_space_dim}
+      <
+      {
+        \dim_min:nn{
+          \dim_max:nn{
+            \box_ht:N\l_tmpa_box
+            +\box_dp:N\l_tmpa_box
+          }{
+            \box_ht:N\l_tmpb_box
+            +\box_dp:N\l_tmpb_box
+            +2\baselineskip
+          }
+        }{
+          \box_ht:N\l_@@_buffer_box
+          +\box_dp:N\l_@@_buffer_box
+        }
+      }
+      {
+%    \end{macrocode}
+%   \textit{Case: not enough space in the current column.} Start a new column and, unless this is a top-level
+%   entry, insert the appropriate continuation into the \cs{l_@@_buffer_box}.
+%    \begin{macrocode}
+        \@@_new_column:
+        \int_compare:nNnT{#1}>{0}{
+          \@@_vbox_prepend:Nn\l_@@_buffer_box{
+            \@@_make_continuation:n{\l_@@_level_int-1}
+          }
+        }
+      }
+%    \end{macrocode}
+%   \cs{l_@@_buffer_box} now contains the material that should be typeset onto the page, and there is enough space in
+%   the current column to typeset a minimal acceptable portion of it. Typesetting is handled by \cs{@@_set_buffer:},
+%   which may use \tn{vsplit}, which produces a warning `\texttt{Underfull \tn{vbox} (badness 10000)}' with even a
+%   \qty{1}{\spoint} mismatch between available breakpoints and desired height. For efficiency, \cs{@@_set_buffer:} uses
+%   an approximation to the desired height, so set \tn{vbadness} to 10000 to suppress these warnings.
+%    \begin{macrocode}
+    \group_begin:
+    \int_set:Nn\vbadness{10000}
+    \@@_typeset_buffer:
+    \group_end:
+%    \end{macrocode}
+%   Finally, clear any stored group.
+%    \begin{macrocode}
+    \tl_clear:N\l_@@_group_tl
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsubsection{Filtering cross-references and locations}
+%
+% In each entry, cross-references and locations may be mixed up and need to be filtered into separate lists.
+%
+% \begin{macro}{
+%   \@@_filter_crossref_location_seqs:n
+% }
+%   Use the supplied list of cross-references and location (\param{1}), which is a clist, to populate
+%   \cs{l_@@_crossref_seq} and \cs{l_@@_location_seq}. The list of macros that are the first tokens in
+%   cross-references is contained in \cs{l_@@_crossref_macros_tl}. All that is required is to check whether the head
+%   token in each item in the \param{1} is in \cs{l_@@_crossref_macros_tl}. and assign that item to the correct seq.
+%    \begin{macrocode}
+\cs_new:Npn\@@_filter_crossref_location_seqs:n #1
+  {
+    \seq_clear:N\l_@@_crossref_seq
+    \seq_clear:N\l_@@_location_seq
+
+    \clist_map_inline:nn{#1}
+      {
+        \tl_if_in:NoTF
+          \l_@@_crossref_macros_tl{ \tl_head:w ##1 {} \q_stop }
+          {
+            \seq_put_right:Nn\l_@@_crossref_seq{##1}
+          }
+          {
+            \seq_put_right:Nn\l_@@_location_seq{##1}
+          }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsubsection{Style}
+%
+% \begin{macro}{
+%   \@@_set_style:n,
+% }
+%   Set up the style for an index entry at the given level.
+%    \begin{macrocode}
+\cs_new:Npn \@@_set_style:n #1
+  {
+    \tl_use:c{l_@@_level_#1_style_tl}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsubsection{Typesetting keywords}
+%
+% \begin{macro}{
+%   \indextrakeyword
+% }
+%   Take a level (\param{1}) and a keyword (\param{2}) and return a (possibly styled) keyword.
+%    \begin{macrocode}
+\cs_new:Npn\indextrakeyword #1#2
+  {
+    #2
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsubsection{Typesetting cross-references}
+%
+% \begin{macro}{
+%   \@@_typeset_between:
+% }
+%   Typeset everything between the keyword and the locations. If there are no cross-references, this means typesetting
+%   just the keyword--location separator; otherwise, it means typesetting the cross-references with the relevant
+%   separators before, after, and between.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_between:
+  {
+    \seq_if_empty:NTF\l_@@_crossref_seq
+      {
+        \tl_use:N\l_@@_separator_kw_loc_tl
+      }
+      {
+        \tl_use:N\l_@@_separator_kw_cr_tl
+        \seq_use:Nn
+          \l_@@_crossref_seq
+          { \tl_use:N\l_@@_separator_cr_cr_tl }
+        \seq_if_empty:NF\l_@@_location_seq
+          { \tl_use:N\l_@@_separator_cr_loc_tl }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsubsection{Typesetting locations}
+%
+% \begin{variable}{
+%   \l_@@_first_item_bool,
+% }
+%   Boolean variable to track whether the current location is the first one in the entry, so that a separator can be
+%   inserted before every non-first location.
+%    \begin{macrocode}
+\bool_new:N\l_@@_first_item_bool
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{
+%   \@@_typeset_locations:
+% }
+%   Typeset locations stored in \cs{l_@@_locations_seq}. Using \cs{seq_use:Nn} is not enough, because each entry must be
+%   processed separately. Thus separators must be inserted `manually'.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_locations:
+  {
+    \bool_set_true:N\l_@@_first_item_bool
+    \seq_map_inline:Nn\l_@@_location_seq
+      {
+        \bool_if:nF{\l_@@_first_item_bool}
+          { \tl_use:N\l_@@_separator_loc_loc_tl }
+        \@@_typeset_aux_location:n{##1}
+        \bool_set_false:N\l_@@_first_item_bool
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_typeset_first location:
+% }
+%   Typeset the first location stored in \cs{l_@@_locations_seq}.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_first_location:
+  {
+    \exp_args:Ne\@@_typeset_aux_location:n
+      {\seq_item:Nn\l_@@_location_seq{1}}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_is_nonrange_p:w
+% }
+%   To be called in the form \cs{@@_is_nonrange_p:w}\meta{parameter}\ttdashdash\cs{q_stop}; uses \TeX\ parsing to check
+%   whether \meta{parameter} does not contain a range marker \ttdashdash.
+%    \begin{macrocode}
+\cs_new:Npn\@@_is_nonrange_p:w #1--#2\q_stop
+  {
+    \tl_if_empty_p:n{#2}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \cs_new:Npn\@@_typeset_ist_location:n
+% }
+%   Typeset a \MakeIndex- or \upmendex-style location. The location might be a
+%   \begin{itemize}
+%     \item a single page;
+%     \item an encapsulated single page \cs{encapsulation}\mcode{\{}\meta{page}\mcode{\}};
+%     \item a range; or
+%     \item an encapsulated range \cs{encapsulation}\mcode{\{}\meta{start}\ttdashdash\meta{end}\mcode{\}}.
+%   \end{itemize}
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_ist_location:n #1
+  {
+%    \end{macrocode}
+%   Check for an encapsulation (a leading control sequence, comparing catcode with \cs{prg_do_nothing:} simply as a
+%   convenience). Either call \cs{@@_typeset_ist_encap_page_or_range:Nn} with this encapsulation or with a `do-nothing'
+%   encapsulation and the braced parameter.
+%    \begin{macrocode}
+    \tl_if_head_eq_catcode:nNTF{#1}\prg_do_nothing:
+      {
+        \@@_typeset_ist_encap_page_or_range:Nn #1
+      }
+      {
+        \@@_typeset_ist_encap_page_or_range:Nn \prg_do_nothing:{#1}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_typeset_ist_encap_page_or_range:Nn
+% }
+%   Take an encapsulation and a parameter and typeset the parameter as a page or range, as appropriate.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_ist_encap_page_or_range:Nn #1#2
+  {
+    \bool_if:nTF{\@@_is_nonrange_p:w #2--\q_stop}
+      {
+%    \end{macrocode}
+%   \textit{Case: not a range.} Parameter \param{2} a single page.
+%    \begin{macrocode}
+        \indextrapage{#1}{#2}
+      }
+      {
+%    \end{macrocode}
+%   \textit{Case: range.} Parameter \param{2} is a range. Call \cs{@@_typeset_ist_encap_range:w} to parse and
+%   typeset it.
+%    \begin{macrocode}
+        \@@_typeset_ist_encap_range:w#1\q_mark #2\q_stop
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_typeset_ist_encap_range:w
+% }
+%   This is effectively just a helper macro so that \TeX\ parsing can be used to convert the parameters into the form
+%   required by \cs{indextrarange}.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_ist_encap_range:w #1\q_mark #2--#3\q_stop
+  {
+    \indextrarange{#1}{#2}{#3}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \cs_new:Npn\@@_typeset_xdy_location:n
+% }
+%   Typeset a \xindy-style location. The location might be
+%   \begin{itemize}
+%     \item a single page;
+%     \item an encapsulated single page \cs{encapsulation}\mcode{\{}\meta{page}\mcode{\}};
+%     \item a range; or
+%     \item an encapsulated range \cs{encapsulation}\mcode{\{}\meta{start}\mcode{\}}\ttdashdash\cs{encapsulation}\mcode{\{}\meta{end}\mcode{\}}.
+%   \end{itemize}
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_xdy_location:n #1
+  {
+    \bool_if:nTF{\@@_is_nonrange_p:w #1--\q_stop}
+      {
+%    \end{macrocode}
+%   \textit{Case: not a range.} Check for an encapsulation (a leading control sequence, comparing catcode with
+%   \cs{prg_do_nothing:} simply as a convenience). Either call \cs{@@_typeset_ist_encap_page_or_range:Nn} with this
+%   encapsulation or with a `do-nothing' encapsulation and the braced parameter.
+%    \begin{macrocode}
+        \tl_if_head_eq_catcode:nNTF{#1}\prg_do_nothing:
+          {
+            \indextrapage #1
+          }
+          {
+            \indextrapage \prg_do_nothing:{#1}
+          }
+      }
+      {
+%    \end{macrocode}
+%   \textit{Case: range.}
+%    \begin{macrocode}
+        \@@_typeset_xdy_range:w #1\q_stop
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_typeset_xdy_range:w
+% }
+%   To be called in the form \cs{@@_typeset_xdy_range:w}\meta{parameter}\cs{q_stop}, so that \param{1} and \param{2}
+%   will be the start and end of the range, possibly with encapsulations. Check \param{1} for a leading control
+%   sequence. If it exists, assume the same holds for \param{2}, and the parameters are of the form
+%   \cs{encapsulation}\texttt{\{}\meta{start}\texttt{\}} and \cs{encapsulation}\texttt{\{}\meta{end}\texttt{\}} and can
+%   be passed to \cs{@@_typeset_xdy_encap_range:w}. Otherwise, each parameter is a page, so call \cs{indextrarange} with
+%   `do-nothing' encapsulations and braced parameters.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_xdy_range:w #1--#2\q_stop
+  {
+    \tl_if_head_eq_catcode:nNTF{#1}\prg_do_nothing:
+      {
+        \@@_typeset_xdy_encap_range:NnNn #1#2
+      }
+      {
+        \indextrarange\prg_do_nothing:{#1}{#2}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \@@_typeset_xdy_encap_range:NnNn
+% }
+%   This is effectively just a helper macro so that \TeX\ parsing can be used to convert the parameters into the form
+%   required by \cs{indextrarange}.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_xdy_encap_range:NnNn #1#2#3#4
+  {
+    \indextrarange{#1}{#2}{#4}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{
+%   \@@_page_basic:nn,
+%   \@@_range_basic:nnn,
+%   \@@_page_hyperref:nn,
+%   \@@_range_hyperref:nnn,
+% }
+%   Default macros for typesetting a page and a range, depending on whether \pkg{hyperref} is loaded.
+%    \begin{macrocode}
+\cs_new:Npn\@@_page_basic:nn #1#2
+  {
+    #1{#2}
+  }
+\cs_new:Npn\@@_range_basic:nnn #1#2#3
+  {
+    #1{#2}--#1{#3}
+  }
+\cs_new:Npn\@@_page_hyperref:nn #1#2
+  {
+    #1{\hyperpage{#2}}
+  }
+\cs_new:Npn\@@_range_hyperref:nnn #1#2#3
+  {
+    #1{\hyperpage{#2}}--#1{\hyperpage{#3}}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% Set \cs{indextrapage} and \cs{indextrarange} to the `basic' versions of the above macros. They may be set to the
+% `hyperref' versions at the start of the \env{theindextra} environment depending on user settings.
+%    \begin{macrocode}
+\cs_set_eq:NN\indextrapage\@@_page_basic:nn
+\cs_set_eq:NN\indextrarange\@@_range_basic:nnn
+%    \end{macrocode}
+%
+%
+% \subsection{Continuations}
+%
+% \begin{macro}{
+%   \@@_make_continuation:n
+% }
+%   Return a continuation text for the level specified in \param{1}, indicating that this (and higher) levels are
+%   continued from the previous column. The continuation text is created using the stored keywords with
+%   \cs{indextramakecontinuation} applied to each.
+%    \begin{macrocode}
+\cs_new:Npn\@@_make_continuation:n #1
+  {
+    \int_step_inline:nnn{0}{#1}{
+      \group_begin:
+      \@@_set_style:n{##1}
+      \leavevmode\strut
+      \indextramakecontinuation{##1}{\tl_use:c{l_@@_saved_keyword_##1}}
+      \strut\par
+      \group_end:
+    }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \indextramakecontinuation
+% }
+%   Take a level (\param{1}) and a keyword (\param{2}) and return a continuation text.
+%    \begin{macrocode}
+\cs_new:Npn\indextramakecontinuation #1#2
+  {
+    #2~(cont.)
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsection{Marks}
+%
+% Define a new mark class for use by \pkg{indextra}.
+%    \begin{macrocode}
+\mark_new_class:n{indextra}
+%    \end{macrocode}
+%
+% \begin{macro}{
+%   \@@_mark_insert:
+% }
+%   Insert the top-level saved keyword as a mark.
+%    \begin{macrocode}
+\cs_new:Npn\@@_mark_insert:
+  {
+    \mark_insert:nn{indextra}{\tl_use:c{l_@@_saved_keyword_0}}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \indextramakemark
+% }
+%   Macro to process an \pkg{indextra} mark for use. Any mark retrieved by \cs{indextrafirstmark} or
+%   \cs{indextralastmark} will have \cs{indextramakemark} applied.
+%    \begin{macrocode}
+\cs_new:Npn\indextramakemark #1
+  {
+    #1
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{
+%   \indextrafirstmark,
+%   \indextralastmark,
+% }
+%   Retrieve the first and last \pkg{indextra} marks on the page, where `first' and `last' have their standard \LaTeX\
+%   meanings. The marks will be processed by \cs{indextramakemark}.
+%    \begin{macrocode}
+\cs_new:Npn\indextrafirstmark
+  {
+    \indextramakemark{ \mark_use_first:nn{page}{indextra} }
+  }
+\cs_new:Npn\indextralastmark
+  {
+    \indextramakemark{ \mark_use_last:nn{page}{indextra} }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \subsection{Buffer}
+%
+% \begin{variable}{
+%   \l_@@_buffer_box,
+% }
+%   Box variable to hold remaining material to be typeset for the current index entry, sub-entry, or sub-sub-entry.
+%    \begin{macrocode}
+\box_new:N\l_@@_buffer_box
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{
+%   \@@_typeset_buffer:
+% }
+%   Output material that has been typeset into \cs{l_@@_buffer_box} and update \cs{g_@@_remaining_space_dim} as
+%   necessary. If necessary, split off as much material as will fit in the current column from \cs{l_@@_buffer_box},
+%   typeset it, add continuation text to the top of \cs{l_@@_buffer_box}, then recursively call this macro. The
+%   recursion ends when all material in \cs{l_@@_buffer_box} has been output.
+%    \begin{macrocode}
+\cs_new:Npn\@@_typeset_buffer:
+  {
+%    \end{macrocode}
+%   Insert a mark. This is either on the first call, or a recursive call, in which case the current position is at the
+%   top of the column. A mark should be inserted in any case.
+%    \begin{macrocode}
+    \@@_mark_insert:
+%    \end{macrocode}
+%   Check whether it is necessary to split off some material for this column and start a new one. There is no need to
+%   check about the minimum acceptable split: if necessary, a new column has already been started in \cs{@@_entry:nnn}.
+%    \begin{macrocode}
+    \dim_compare:nNnT
+    {\g_@@_remaining_space_dim}
+    <
+    {\box_ht:N\l_@@_buffer_box+\box_dp:N\l_@@_buffer_box}
+      {
+%    \end{macrocode}
+%   \textit{Case: A split is necessary.} Compute an approximation (definitely too large, but acceptably so) to the
+%   amount to be split off, then split and rebox.
+%    \begin{macrocode}
+        \dim_set:Nn
+          \l_tmpa_dim{\g_@@_remaining_space_dim-.3\baselineskip}
+        \vbox_set_split_to_ht:NNn
+          \l_tmpa_box\l_@@_buffer_box{\l_tmpa_dim}
+        \vbox_set:Nn
+          \l_tmpa_box{\vbox_unpack_drop:N\l_tmpa_box}
+%    \end{macrocode}
+%   Update \cs{g_@@_remaining_space_dim} and output the split material.
+%    \begin{macrocode}
+        \dim_gset:Nn\g_@@_remaining_space_dim
+          {
+            \g_@@_remaining_space_dim
+            -\box_ht:N\l_tmpa_box
+            -\box_dp:N\l_tmpa_box
+          }
+        \vbox_unpack_drop:N\l_tmpa_box
+%    \end{macrocode}
+%   Add a continuation text to the top of \cs{l_@@_buffer_box}, start a new column, and recurse.
+%    \begin{macrocode}
+        \@@_vbox_prepend:Nn
+          \l_@@_buffer_box
+          {\@@_make_continuation:n{\l_@@_level_int}}
+        \@@_new_column:
+        \@@_typeset_buffer:
+      }
+      {
+%    \end{macrocode}
+%   \textit{Case: No split is necessary.} Update \cs{g_@@_remaining_space_dim} and output the buffer contents.
+%    \begin{macrocode}
+        \dim_gset:Nn\g_@@_remaining_space_dim
+          {
+            \g_@@_remaining_space_dim
+            -\box_ht:N\l_@@_buffer_box
+            -\box_dp:N\l_@@_buffer_box
+          }
+        \vbox_unpack_drop:N\l_@@_buffer_box
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{
+%   \@@_vbox_prepend
+% }
+%   Takes a vertical box variable as \param{1} and typeset the material in \param{2} into it above the original content.
+%    \begin{macrocode}
+\cs_new:Npn\@@_vbox_prepend:Nn #1#2
+{
+  \vbox_set:Nn #1{
+    \vbox{#2}
+    \vbox_unpack_drop:N #1
+  }
+}
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+%</package>
+%    \end{macrocode}
+%
+%
+%
+% \subsection{Style files}
+%
+% \subsubsection{\texorpdfstring{\file{.ist}}{ist} -- \MakeIndex, \upmendex}
+%
+%    \begin{macrocode}
+%<*ist>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+page_precedence "ArRn"
+
+preamble        "\\begin{theindextra}\n  \\indextrastyleversion{0.1}\n  \\indextraprocessortype{ist}\n"
+postamble       "\n\n\n\\end{theindextra}\n"
+
+group_skip      "\n\n\n  \\indextraspace"
+
+heading_prefix  "\n\n\n  \\indextragroup{"
+heading_suffix  "}\n"
+headings_flag   1
+
+item_0          "\n  \\indextraentry{0}{"
+item_1          "\n    \\indextraentry{1}{"
+item_01          "\n    \\indextraentry{1}{"
+item_x1          "}{}\n    \\indextraentry{1}{"
+item_2          "\n      \\indextraentry{2}{"
+item_12          "\n    \\indextraentry{2}{"
+item_x2          "}{}\n    \\indextraentry{2}{"
+
+delim_0         "}{"
+delim_1         "}{"
+delim_2         "}{"
+delim_t         "}"
+
+encap_prefix    "\\"
+encap_infix     "{"
+encap_suffix    "}"
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%</ist>
+%    \end{macrocode}
+%
+%
+%
+% \subsubsection{\texorpdfstring{\file{.xdy}}{xdy} -- \xindy}
+%
+%    \begin{macrocode}
+%<*xdy>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+(define-attributes (("textit" "defterm")))
+
+(markup-index :open  "\begin{theindextra}~n  \indextrastyleversion{0.1}~n  \indextraprocessortype{xdy}~n"
+              :close "~n~n\end{theindextra}~n"
+              :tree)
+
+(markup-letter-group-list :sep "~n  \indextraspace~n")
+(markup-letter-group :open-head "~n  % "
+                     :close-head "~n"
+                     :group "default")
+(markup-letter-group :open-head "~n  \indextragroup{"
+                     :close-head "}~n~n")
+
+(markup-indexentry :open "  \indextraentry{0}{"
+                   :close "}%~n"
+                   :depth 0)
+(markup-indexentry :open "}%~n    \indextraentry{1}{"
+                   :close ""
+                   :depth 1)
+(markup-indexentry :open "}%~n      \indextraentry{2}{"
+                   :close ""
+                   :depth 2)
+
+(markup-keyword-list :open "" :close "}{" :sep ";")
+
+(markup-locclass-list :open "" :sep ", " :close "")
+(markup-locref-list :open ""  :sep ", " :close "")
+
+(markup-range :sep "--")
+(markup-locref :open "\textit{" :close "}" :attr "textit")
+(markup-locref :open "\defterm{"  :close "}" :attr "defterm")
+
+(markup-crossref-list :class "see"
+                      :open "\see{"
+                      :sep "}{}, \see{"
+                      :close "}{}")
+(markup-crossref-list :class "seealso"
+                      :open "\seealso{"
+                      :sep "}{}, \seealso{"
+                      :close "}{}")
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%</xdy>
+%    \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
+%
+%
+%
+% \iffalse
+%<*metadriver>
+\input{indextra.dtx}
+%</metadriver>
+%
+%<*demo>
+\documentclass{article}
+
+\usepackage[
+paperwidth=105mm,
+paperheight=100mm,
+inner=5mm,
+width=90mm,
+height=90mm,
+top=5mm,
+nohead,
+nofoot,
+columnsep=5mm,
+twocolumn,
+]{geometry}
+
+\usepackage{xcolor}
+\definecolor{bg}{gray}{0.9}
+\pagecolor{bg}
+
+
+\parindent=1em
+
+\usepackage{indextra}
+
+\indextrasetup{
+  before code={},
+  after code={},
+}
+
+
+\newcommand\see[1]{\textit{see}~`#1'}
+\newcommand\seealso[1]{\textit{see also}~`#1'}
+
+
+\raggedright
+\pagestyle{empty}
+
+\begin{document}
+
+\begin{theindextra}
+  \indextrastyleversion{0.1}
+  \indextraprocessortype{ist}
+
+  \indextraentry{0}{neutron}{855, 862, 885--886}%
+  \indextraentry{0}{\textit{New Astronomy} (Kepler)}{\see{\textit{Astronomia Nova}}{}}%
+  \indextraentry{0}{Newtonian physics}{298, 376, 390, 589, 813, 830, 834, 842, 891, 894--895}%
+  \indextraentry{0}{\textit{Nicomachean Ethics} (Aristotle)}{85}%
+  \indextraentry{0}{Nim}{11}%
+  \indextraentry{0}{nine-point circle}{584, 641, 662--663}%
+  \indextraentry{0}{Nobel Prize}{}%
+    \indextraentry{1}{chemistry}{495, 566, 815, 865, 937}%
+    \indextraentry{1}{literature}{485}%
+    \indextraentry{1}{peace}{865}%
+    \indextraentry{1}{physics}{502, 598, 727, 834, 836, 838, 851, 853--854, 856, 865, 867, 877, 893, 903, 905, 918, 921}%
+  \indextraentry{0}{nobility}{188, 240, 250, 464}%
+  \indextraentry{0}{non-aesthetic property}{700, 715}%
+  \indextraentry{0}{non-associative algebra}{848}%
+  \indextraentry{0}{non-commutativity}{643}%
+  \indextraentry{0}{non-constructive proof}{787}%
+  \indextraentry{0}{non-determinism}{595}%
+  \indextraentry{0}{non-euclidean geometry}{380, 401, 404--408, 436, 628, 643, 880, 896}%
+  \indextraentry{0}{non-linearity}{882}%
+  \indextraentry{0}{non-measurable set}{672}%
+  \indextraentry{0}{non-sensory property}{695}%
+  \indextraentry{0}{non-visual thinking}{633}%
+  \indextraentry{0}{nonagon}{220}%
+
+\end{theindextra}
+
+\end{document}
+% </demo>
+% \fi


Property changes on: branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.ins
===================================================================
--- branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.ins	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/source/latex/indextra/indextra.ins	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,65 @@
+%%
+%% Copyright (C) 2025 Alan J. Cain
+%%
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%%
+%%    http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.3c or later is part of all distributions of
+%% LaTeX version 2008-05-04 or later.
+%%
+\input l3docstrip.tex
+\askforoverwritefalse
+\nopostamble
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2025 Alan J. Cain
+
+This file may be distributed and/or modified under the
+conditions of the LaTeX Project Public License, either
+version 1.3c of this license or (at your option) any later
+version. The latest version of this license is in:
+
+http://www.latex-project.org/lppl.txt
+
+and version 1.3c or later is part of all distributions of
+LaTeX version 2008-05-04 or later.
+
+\endpreamble
+
+\generate{\file{indextra.sty}{\from{indextra.dtx}{package}}}
+\generate{\file{indextra-doc.tex}{\from{indextra.dtx}{metadriver}}}
+\generate{\file{indextra-doc-demo.tex}{\from{indextra.dtx}{demo}}}
+\generate{\file{indextra.ist}{\from{indextra.dtx}{ist}}}
+
+
+\def\MetaPrefix{;;}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2025 Alan J. Cain
+
+This file may be distributed and/or modified under the
+conditions of the LaTeX Project Public License, either
+version 1.3c of this license or (at your option) any later
+version. The latest version of this license is in:
+
+http://www.latex-project.org/lppl.txt
+
+and version 1.3c or later is part of all distributions of
+LaTeX version 2008-05-04 or later.
+
+\endpreamble
+
+\generate{\file{indextra.xdy}{\from{indextra.dtx}{xdy}}}
+
+
+\endbatchfile

Added: branches/branch2024.final/Master/texmf-dist/tex/latex/indextra/indextra.sty
===================================================================
--- branches/branch2024.final/Master/texmf-dist/tex/latex/indextra/indextra.sty	                        (rev 0)
+++ branches/branch2024.final/Master/texmf-dist/tex/latex/indextra/indextra.sty	2025-02-27 20:15:53 UTC (rev 74328)
@@ -0,0 +1,464 @@
+%%
+%% This is file `indextra.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% indextra.dtx  (with options: `package')
+%% 
+%% This is a generated file.
+%% 
+%% Copyright (C) 2025 Alan J. Cain
+%% 
+%% This file may be distributed and/or modified under the
+%% conditions of the LaTeX Project Public License, either
+%% version 1.3c of this license or (at your option) any later
+%% version. The latest version of this license is in:
+%% 
+%% http://www.latex-project.org/lppl.txt
+%% 
+%% and version 1.3c or later is part of all distributions of
+%% LaTeX version 2008-05-04 or later.
+%% 
+\NeedsTeXFormat{LaTeX2e}[2020-02-02]
+\ProvidesExplPackage{indextra}{2025-02-26}{0.21.2}
+  {Enhanced index typesetting}
+\keys_define:nn { indextra }
+{
+  before~code .tl_set:N = \l__indextra_before_code_tl,
+  after~code .tl_set:N = \l__indextra_after_code_tl,
+  before~code .initial:n = {\begin{theindex}},
+  after~code .initial:n = {\end{theindex}},
+}
+\keys_define:nn { indextra }
+{
+  level~0~style .tl_set:c = {l__indextra_level_0_style_tl},
+  level~1~style .tl_set:c = {l__indextra_level_1_style_tl},
+  level~2~style .tl_set:c = {l__indextra_level_2_style_tl},
+  level~0~style .initial:n = {\parindent=0em\hangindent=3.75em},
+  level~1~style .initial:n = {\parindent=1.5em\hangindent=5.25em},
+  level~2~style .initial:n = {\parindent=3em\hangindent=6.75em},
+}
+\keys_define:nn { indextra }
+{
+  separator~keyword~crossref .tl_set:N = \l__indextra_separator_kw_cr_tl,
+  separator~keyword~location .tl_set:N = \l__indextra_separator_kw_loc_tl,
+  separator~crossref~crossref .tl_set:N = \l__indextra_separator_cr_cr_tl,
+  separator~crossref~location .tl_set:N = \l__indextra_separator_cr_loc_tl,
+  separator~location~location .tl_set:N = \l__indextra_separator_loc_loc_tl,
+  separator~keyword~crossref .initial:n = {\break},
+  separator~keyword~location .initial:n = {\space\space},
+  separator~crossref~crossref .initial:n = {\break},
+  separator~crossref~location .initial:n = {\break},
+  separator~location~location .initial:n = {,~},
+}
+\keys_define:nn { indextra }
+{
+  crossref~macros .tl_set:N = \l__indextra_crossref_macros_tl,
+  crossref~macros .initial:n = {\see\seealso},
+}
+\keys_define:nn { indextra }
+{
+  hyperindex .bool_set:N = \l__indextra_hyperindex_bool,
+  hyperindex .initial:n = {true},
+}
+\keys_define:nn { indextra }
+{
+  bookmarks .bool_set:N = \l__indextra_bookmarks_bool,
+  bookmarks .initial:n = {true},
+}
+\keys_define:nn { indextra }
+{
+  headings .bool_set:N = \l__indextra_headings_bool,
+  headings .initial:n = {true},
+}
+\NewDocumentCommand{\indextrasetup}{ m }
+{
+  \keys_set:nn{ indextra }{ #1 }
+}
+\NewDocumentEnvironment{theindextra}{}
+  { \__indextra_main_begin: }
+  { \__indextra_main_end: }
+\cs_new:Npn \__indextra_main_begin:
+{
+  \group_begin:
+  \tl_use:N\l__indextra_before_code_tl
+  \dim_gset:Nn\g__indextra_remaining_space_dim{\@colht}
+  \dim_set:Nn\splittopskip{.7\baselineskip}
+  \dim_set:Nn\topskip{.7\baselineskip}
+  \dim_set:Nn\parskip{0pt}
+  \cs_set_eq:NN\indextrastyleversion\__indextra_style_version:n
+  \cs_set_eq:NN\indextraprocessortype\__indextra_processor_type:n
+  \cs_set_eq:NN\indextraspace\__indextra_space:
+  \cs_set_eq:NN\indextragroup\__indextra_group:n
+  \cs_set_eq:NN\indextraentry\__indextra_entry:nnn
+  \bool_if:nF
+    { \l__indextra_bookmarks_bool && \cs_if_exist_p:N\belowpdfbookmark }
+    {
+      \cs_set_eq:NN\__indextra_bookmark_stored_group:\prg_do_nothing:
+    }
+  \bool_if:NF\l__indextra_headings_bool
+    {
+      \cs_set_eq:NN\__indextra_typeset_stored_group_heading:\prg_do_nothing:
+    }
+  \bool_if:nT{ \l__indextra_hyperindex_bool && \cs_if_exist_p:N\hyperpage }
+    {
+      \cs_if_eq:NNT\indextrapage\__indextra_page_basic:nn
+        {
+          \cs_set_eq:NN\indextrapage\__indextra_page_hyperref:nn
+        }
+      \cs_if_eq:NNT\indextrarange\__indextra_range_basic:nnn
+        {
+          \cs_set_eq:NN\indextrarange\__indextra_range_hyperref:nnn
+        }
+    }
+}
+\cs_new:Npn \__indextra_main_end:
+{
+  \tl_use:N\l__indextra_after_code_tl
+  \group_end:
+}
+\msg_new:nnn{ indextra }{ incompatible_version }
+  { The~.ind~file~was~generated~using~a~style~from~a~different~version~of~indextra. }
+\cs_new:Npn\__indextra_style_version:n #1
+{
+  \str_if_eq:nnF{#1}{0.1}
+    {
+      \msg_error:nn{ indextra }{ incompatible_version }
+    }
+}
+\cs_new:Npn\__indextra_processor_type:n #1
+{
+  \cs_set_eq:Nc\__indextra_typeset_aux_location:n
+    { __indextra_typeset_#1_location:n }
+}
+\dim_new:N\g__indextra_remaining_space_dim
+\cs_new:Npn\__indextra_new_column:
+  {
+    \vfill
+    \newpage
+    \dim_gset:Nn\g__indextra_remaining_space_dim{\@colht}
+  }
+\cs_new:Npn\__indextra_space:
+  {
+    \dim_compare:nNnT{\g__indextra_remaining_space_dim}<{\baselineskip}{
+      \__indextra_new_column:
+    }
+    \vbox_set:Nn\l__indextra_buffer_box{\leavevmode\strut\par}
+    \__indextra_typeset_buffer:
+  }
+\tl_new:N\l__indextra_group_tl
+\cs_new:Npn\__indextra_group:n #1
+  {
+    \tl_set:Nn\l__indextra_group_tl{#1}
+  }
+\int_new:N\g__indextra_group_bookmark_int
+\cs_new:Npn\__indextra_bookmark_stored_group:
+  {
+    \tl_if_empty:NF\l__indextra_group_tl
+      {
+        \int_gincr:N\g__indextra_group_bookmark_int
+        \belowpdfbookmark
+          {\tl_use:N\l__indextra_group_tl}
+          {pdf:indextra:\int_value:w\g__indextra_group_bookmark_int}
+      }
+  }
+\cs_new:Npn\__indextra_typeset_stored_group_heading:
+  {
+    \tl_if_empty:NF\l__indextra_group_tl
+      {
+        \group_begin:
+        \noindent\strut
+        \indextramakegroupheading{\tl_use:N\l__indextra_group_tl}
+        \strut\par
+        \group_end:
+      }
+  }
+\cs_new:Npn\indextramakegroupheading #1
+  {
+    \textbf{#1}
+  }
+\int_new:N\l__indextra_level_int
+\tl_new:c{l__indextra_saved_keyword_0}
+\tl_new:c{l__indextra_saved_keyword_1}
+\tl_new:c{l__indextra_saved_keyword_2}
+\seq_new:N\l__indextra_crossref_seq
+\seq_new:N\l__indextra_location_seq
+\cs_new:Npn\__indextra_entry:nnn #1#2#3
+  {
+    \int_set:Nn\l__indextra_level_int{#1}
+    \tl_set:cn{l__indextra_saved_keyword_#1}{#2}
+    \__indextra_filter_crossref_location_seqs:n{#3}
+    \vbox_set:Nn\l__indextra_buffer_box{
+      \__indextra_bookmark_stored_group:
+      \__indextra_typeset_stored_group_heading:
+      \group_begin:
+      \__indextra_set_style:n{#1}
+      \leavevmode
+      \strut
+      \indextrakeyword{#1}{#2}
+      \__indextra_typeset_between:
+      \__indextra_typeset_locations:
+      \strut
+      \par
+      \goodbreak
+      \group_end:
+    }
+    \vbox_set:Nn\l_tmpa_box{
+      \__indextra_typeset_stored_group_heading:
+      \group_begin:
+      \__indextra_set_style:n{#1}
+      \leavevmode
+      \strut
+      \indextrakeyword{#1}{#2}
+      \__indextra_typeset_between:
+      \__indextra_typeset_first_location:
+      \strut
+      \par
+      \group_end:
+    }
+    \vbox_set:Nn\l_tmpb_box{
+      \__indextra_typeset_stored_group_heading:
+    }
+    \dim_compare:nNnT
+      {\g__indextra_remaining_space_dim}
+      <
+      {
+        \dim_min:nn{
+          \dim_max:nn{
+            \box_ht:N\l_tmpa_box
+            +\box_dp:N\l_tmpa_box
+          }{
+            \box_ht:N\l_tmpb_box
+            +\box_dp:N\l_tmpb_box
+            +2\baselineskip
+          }
+        }{
+          \box_ht:N\l__indextra_buffer_box
+          +\box_dp:N\l__indextra_buffer_box
+        }
+      }
+      {
+        \__indextra_new_column:
+        \int_compare:nNnT{#1}>{0}{
+          \__indextra_vbox_prepend:Nn\l__indextra_buffer_box{
+            \__indextra_make_continuation:n{\l__indextra_level_int-1}
+          }
+        }
+      }
+    \group_begin:
+    \int_set:Nn\vbadness{10000}
+    \__indextra_typeset_buffer:
+    \group_end:
+    \tl_clear:N\l__indextra_group_tl
+  }
+\cs_new:Npn\__indextra_filter_crossref_location_seqs:n #1
+  {
+    \seq_clear:N\l__indextra_crossref_seq
+    \seq_clear:N\l__indextra_location_seq
+
+    \clist_map_inline:nn{#1}
+      {
+        \tl_if_in:NoTF
+          \l__indextra_crossref_macros_tl{ \tl_head:w ##1 {} \q_stop }
+          {
+            \seq_put_right:Nn\l__indextra_crossref_seq{##1}
+          }
+          {
+            \seq_put_right:Nn\l__indextra_location_seq{##1}
+          }
+      }
+  }
+\cs_new:Npn \__indextra_set_style:n #1
+  {
+    \tl_use:c{l__indextra_level_#1_style_tl}
+  }
+\cs_new:Npn\indextrakeyword #1#2
+  {
+    #2
+  }
+\cs_new:Npn\__indextra_typeset_between:
+  {
+    \seq_if_empty:NTF\l__indextra_crossref_seq
+      {
+        \tl_use:N\l__indextra_separator_kw_loc_tl
+      }
+      {
+        \tl_use:N\l__indextra_separator_kw_cr_tl
+        \seq_use:Nn
+          \l__indextra_crossref_seq
+          { \tl_use:N\l__indextra_separator_cr_cr_tl }
+        \seq_if_empty:NF\l__indextra_location_seq
+          { \tl_use:N\l__indextra_separator_cr_loc_tl }
+      }
+  }
+\bool_new:N\l__indextra_first_item_bool
+\cs_new:Npn\__indextra_typeset_locations:
+  {
+    \bool_set_true:N\l__indextra_first_item_bool
+    \seq_map_inline:Nn\l__indextra_location_seq
+      {
+        \bool_if:nF{\l__indextra_first_item_bool}
+          { \tl_use:N\l__indextra_separator_loc_loc_tl }
+        \__indextra_typeset_aux_location:n{##1}
+        \bool_set_false:N\l__indextra_first_item_bool
+      }
+  }
+\cs_new:Npn\__indextra_typeset_first_location:
+  {
+    \exp_args:Ne\__indextra_typeset_aux_location:n
+      {\seq_item:Nn\l__indextra_location_seq{1}}
+  }
+\cs_new:Npn\__indextra_is_nonrange_p:w #1--#2\q_stop
+  {
+    \tl_if_empty_p:n{#2}
+  }
+\cs_new:Npn\__indextra_typeset_ist_location:n #1
+  {
+    \tl_if_head_eq_catcode:nNTF{#1}\prg_do_nothing:
+      {
+        \__indextra_typeset_ist_encap_page_or_range:Nn #1
+      }
+      {
+        \__indextra_typeset_ist_encap_page_or_range:Nn \prg_do_nothing:{#1}
+      }
+  }
+\cs_new:Npn\__indextra_typeset_ist_encap_page_or_range:Nn #1#2
+  {
+    \bool_if:nTF{\__indextra_is_nonrange_p:w #2--\q_stop}
+      {
+        \indextrapage{#1}{#2}
+      }
+      {
+        \__indextra_typeset_ist_encap_range:w#1\q_mark #2\q_stop
+      }
+  }
+\cs_new:Npn\__indextra_typeset_ist_encap_range:w #1\q_mark #2--#3\q_stop
+  {
+    \indextrarange{#1}{#2}{#3}
+  }
+\cs_new:Npn\__indextra_typeset_xdy_location:n #1
+  {
+    \bool_if:nTF{\__indextra_is_nonrange_p:w #1--\q_stop}
+      {
+        \tl_if_head_eq_catcode:nNTF{#1}\prg_do_nothing:
+          {
+            \indextrapage #1
+          }
+          {
+            \indextrapage \prg_do_nothing:{#1}
+          }
+      }
+      {
+        \__indextra_typeset_xdy_range:w #1\q_stop
+      }
+  }
+\cs_new:Npn\__indextra_typeset_xdy_range:w #1--#2\q_stop
+  {
+    \tl_if_head_eq_catcode:nNTF{#1}\prg_do_nothing:
+      {
+        \__indextra_typeset_xdy_encap_range:NnNn #1#2
+      }
+      {
+        \indextrarange\prg_do_nothing:{#1}{#2}
+      }
+  }
+\cs_new:Npn\__indextra_typeset_xdy_encap_range:NnNn #1#2#3#4
+  {
+    \indextrarange{#1}{#2}{#4}
+  }
+\cs_new:Npn\__indextra_page_basic:nn #1#2
+  {
+    #1{#2}
+  }
+\cs_new:Npn\__indextra_range_basic:nnn #1#2#3
+  {
+    #1{#2}--#1{#3}
+  }
+\cs_new:Npn\__indextra_page_hyperref:nn #1#2
+  {
+    #1{\hyperpage{#2}}
+  }
+\cs_new:Npn\__indextra_range_hyperref:nnn #1#2#3
+  {
+    #1{\hyperpage{#2}}--#1{\hyperpage{#3}}
+  }
+\cs_set_eq:NN\indextrapage\__indextra_page_basic:nn
+\cs_set_eq:NN\indextrarange\__indextra_range_basic:nnn
+\cs_new:Npn\__indextra_make_continuation:n #1
+  {
+    \int_step_inline:nnn{0}{#1}{
+      \group_begin:
+      \__indextra_set_style:n{##1}
+      \leavevmode\strut
+      \indextramakecontinuation{##1}{\tl_use:c{l__indextra_saved_keyword_##1}}
+      \strut\par
+      \group_end:
+    }
+  }
+\cs_new:Npn\indextramakecontinuation #1#2
+  {
+    #2~(cont.)
+  }
+\mark_new_class:n{indextra}
+\cs_new:Npn\__indextra_mark_insert:
+  {
+    \mark_insert:nn{indextra}{\tl_use:c{l__indextra_saved_keyword_0}}
+  }
+\cs_new:Npn\indextramakemark #1
+  {
+    #1
+  }
+\cs_new:Npn\indextrafirstmark
+  {
+    \indextramakemark{ \mark_use_first:nn{page}{indextra} }
+  }
+\cs_new:Npn\indextralastmark
+  {
+    \indextramakemark{ \mark_use_last:nn{page}{indextra} }
+  }
+\box_new:N\l__indextra_buffer_box
+\cs_new:Npn\__indextra_typeset_buffer:
+  {
+    \__indextra_mark_insert:
+    \dim_compare:nNnT
+    {\g__indextra_remaining_space_dim}
+    <
+    {\box_ht:N\l__indextra_buffer_box+\box_dp:N\l__indextra_buffer_box}
+      {
+        \dim_set:Nn
+          \l_tmpa_dim{\g__indextra_remaining_space_dim-.3\baselineskip}
+        \vbox_set_split_to_ht:NNn
+          \l_tmpa_box\l__indextra_buffer_box{\l_tmpa_dim}
+        \vbox_set:Nn
+          \l_tmpa_box{\vbox_unpack_drop:N\l_tmpa_box}
+        \dim_gset:Nn\g__indextra_remaining_space_dim
+          {
+            \g__indextra_remaining_space_dim
+            -\box_ht:N\l_tmpa_box
+            -\box_dp:N\l_tmpa_box
+          }
+        \vbox_unpack_drop:N\l_tmpa_box
+        \__indextra_vbox_prepend:Nn
+          \l__indextra_buffer_box
+          {\__indextra_make_continuation:n{\l__indextra_level_int}}
+        \__indextra_new_column:
+        \__indextra_typeset_buffer:
+      }
+      {
+        \dim_gset:Nn\g__indextra_remaining_space_dim
+          {
+            \g__indextra_remaining_space_dim
+            -\box_ht:N\l__indextra_buffer_box
+            -\box_dp:N\l__indextra_buffer_box
+          }
+        \vbox_unpack_drop:N\l__indextra_buffer_box
+      }
+  }
+\cs_new:Npn\__indextra_vbox_prepend:Nn #1#2
+{
+  \vbox_set:Nn #1{
+    \vbox{#2}
+    \vbox_unpack_drop:N #1
+  }
+}


Property changes on: branches/branch2024.final/Master/texmf-dist/tex/latex/indextra/indextra.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: branches/branch2024.final/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc
===================================================================
--- branches/branch2024.final/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc	2025-02-27 20:15:24 UTC (rev 74327)
+++ branches/branch2024.final/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc	2025-02-27 20:15:53 UTC (rev 74328)
@@ -739,6 +739,7 @@
 depend import
 depend incgraph
 depend indextools
+depend indextra
 depend inline-images
 depend inlinedef
 depend inlinegraphicx

Added: branches/branch2024.final/Master/tlpkg/tlpsrc/indextra.tlpsrc
===================================================================


More information about the tex-live-commits mailing list.