% \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@subsection { \@dottedtocline{2}{2.5em}{2.8em} } % #2 = 1.5em \cs_gset:Npn \l@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} % % \fi % % % % \GetFileInfo{indextra.sty} % % \makeatletter % \lstset{ % language=[LaTeX]TeX, % columns=fullflexible, % keepspaces=true, % commentstyle=\slshape, % basicstyle=\ttfamily\lst@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 % % \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} % % \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} % % \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} % % \end{macrocode} % % \end{implementation} % % \PrintIndex % % % % \iffalse %<*metadriver> \input{indextra.dtx} % % %<*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} % % \fi