--- title: "crossrefenum" subtitle: "Smart typesetting of enumerated cross-references for various TeX formats" author: Bastien Dumont date: 2023/02/20 lang: en-US toc: true license: true --- _crossrefenum_ lets TeX manage the formatting of bunches of cross-references for you. It features: * Automatic collapsing of references; * Support for references by various criteria, including page and note number, line number in ConTeXt and edpage and edline when used in conjunction with _reledmac_; * Handling of references combining two criteria (e.g. by page and note number); * Extension mechanisms to add support to other types of references without modifying the internal macros. Note that sorting is not supported. I assume that users know in what order the labels they refer to appear in their document. It is written in Plain TeX as much as possible in order to make it compatible with a wide array of formats. For the moment, it works out of the box with ConTeXt and LaTeX. The file `main-test.pdf` provides a showcase of the abilities of _crossrefenum_. ## Loading To load _crossrefenum_, provided that `crossrefenum.tex`, `crossrefenum.sty` and `t-crossrefenum.tex` are installed in a directory where TeX will find them (presumably under the `tex/generic/crossrefenum` directory of one of your _texmf_ trees), you can do: * `\input{crossrefenum}` (generic); * `\usepackage{crossrefenum}` (LaTeX; must be called after _nameref_ if you use _hyperref_); * `\usemodule[crossrefenum]` (ConTeXt). ## Basic invocation The macro `\crossrefenum` has the following syntax: > \\crossrefenum\[_type_\]\[_print prefix?_\]{_enumeration_} * _type_ is the type of reference. Built-in possible values are: * For LaTeX and ConTeXt: `page`, `note`, `pagenote`; * For ConTeXt only: `line`, `pageline`; * For LaTeX with _reledmac_: `edpage`, `edline`, `edpageline`. * _print prefix?_ indicates whether the prefix (like “p. ”) should be printed or not: true if set to `withprefix` or `yes`; * _enumeration_ is a group containing one or more _single_ labels (e.g. `{mylabel}`) or _ranges_ (e.g. `{lbl-begin to lbl-end}`) included in groups. Ranges cannot be used with `note` and `pagenote` types. _type_ and _print prefix?_ are optional. _type_ defaults to `page` and _print prefix_ to `withprefix`. Here are some valid invocations: * `\crossrefenum[note][withprefix]{{lblone}{lbltwo}{lblthree}}` * `\crossrefenum[edline][noprefix]{{lblone}{lbltwo}{lblthree}}` * `\crossrefenum[noprefix]{{lblone}{lbltwo}{lblthree}}` (_type_ defaults to `page`) * `\crossrefenum[note]{{lblone}{lbltwo}{lblthree}}` (_print prefix?_ defaults to `withprefix`) * `\crossrefenum{{lblone}{lbltwo}{lblthree}}` (_type_ defaults to `page` and _print prefix?_ defaults to `withprefix`) * `\crossrefenum{{only-one}}` (even if the enumeration is limited to one item, it must be inside its own group) ## Customization Customizing is done by redefining configuration macros. We describe the general mechanism first. Macros for double types are introduced at the end of this section. ### Prefixes, delimiters and separators Every simple type has two macros corresponding to the singular and plural prefixes printed before the value of the reference. By default, they are set to: ```{.tex} \def\crfnmPage{p.~} \def\crfnmPages{pp.~} \def\crfnmNote{n.~} \def\crfnmNotes{nn.~} \let\crfnmEdpage\crfnmPage \let\crfnmEdpages\crfnmPages \def\crfnmEdline{l.~} \def\crfnmEdlines{ll.~} ``` Between successive items in an enumeration, `\crossrefenum` calls `\crfnmDefaultEnumDelim` or `\crfnmDefaultBeforeLastInEnum`. By default, they are set to: ```{.tex} \def\crfnmDefaultEnumDelim{, } \def\crfnmDefaultBeforeLastInEnum{ and } ``` The beginning and the end of a range are separated by `\crfnmDefaultRangeSep`. By default: ```{.tex} \def\crfnmDefaultRangeSep{–} ``` ### Collapsable and non-collapsable types {#collapsable-types} The macro `\crfnmDefaultCollapsable` defines if ranges are allowed. The default configuration is: ```{.tex} \def\crfnmDefaultCollapsable{yes} \def\crfnmNoteCollapsable{no} ``` Thus, a reference to consecutive notes is formatted like _nn. 3, 4 and 5_, not like _nn. 3–5_. Ranges are not accepted in the argument of `\crossrefenum` for non-collapsable types. This extends to double types that include a non-collapsable type (such as `pagenote` in the default configuration). ### Double types Two subtypes in a double type (e.g. page and note number for `pagenote`) are separated by `\crfnmDefaultSubtypesSep`. Default: ```{.tex} \def\crfnmDefaultSubtypesSep{, } ``` When more than one reference is cited in an enumeration, you may not want the first prefix to be repeated every time (e.g. you could prefer “pp. 5, n. 2; 7, n. 4” over “p. 5, n. 2; p. 7, n. 4”). In that case, set `\crfnmDefaultPrintFirstPrefix` to `once`. Default is: ```{.tex} \def\crfnmDefaultPrintFirstPrefix{always} ``` If you want to format the second part of the reference in a special way (e.g. in superscript), use `\crfnmDefaultFormatInSecond`, which takes one argument which corresponds to the reference number and all its affixes. Default is: ```{.tex} \def\crfnmDefaultFormatInSecond#1{#1} ``` If you don't want any prefix to be printed in the second term of a double reference, set `\crfnmDefaultPrintPrefixInSecond` to `no` (default is yes). For instance: ```{.tex} \def\crfnmEdlineFormatInSecond#1{\textsuperscript{#1}} \def\crfnmEdlinePrintPrefixInSecond{no} \crossrefenum[edpageline]{{mylabel}} ``` `\noindentation`{=context} may return “p. 5^10^”, while `\crossrefenum[edline]{{mylabel}}` would return “l. 10”. For the second part of such an enumeration (e.g. “l. 10” in “p. 5, l. 10”), you can specify a specific delimiter and a specific string to be printed before the last reference: for instance, you may want to use the word “and” before the last note number if the reference type is a simple one (`note`), and a comma if it is comes in second in a double reference (e.g. in `pagenote`). To achieve this, you should redefine `\crfnmDefaultEnumDelimInSecond` and `\crfnmDefaultBeforeLastInEnumInSecond`. By default, these macros fall back respectively on `\crfnmDefaultEnumDelim` and `\crfnmDefaultBeforeLastInEnum`. When citing a range, the two parts of the reference can either be split (e.g. “p. 5, l. 3 – p. 7, l. 44”) or grouped (“p. 5–7, l. 3–44”). This is controlled via `\crfnmDefaultGroupSubtypes`, which can be set to `yes` or `no`. This works only with [collapsable types](#collapsable-types). Default is: ```{.tex} \def\crfnmDefaultGroupSubtypes{no} ``` To know if a reference to “p. 6, l. 34” should be merged with “p. 7, l. 35”, _crossrefenum_ needs to know if the lineation is continuous (in this case, these lines are consecutive) or per page (they are not, so they should not be merged). You can set accordingly `\crfnmDefaultNumberingContinuousAcrossDocument`[^line-numbering] to `yes` (default) or `no`. Note that _crossrefenum_ cannot merge a reference to the last line of a page and the first line of the following page if the lineation is not continuous. [^line-numbering]: In this case, you could set more specifically `\crfnmLineNumberingContinuousAcrossDocument` or `\crfnmEdlineNumberingContinuousAcrossDocument`: see [the following subsection](#specialize-config-macros). By default, the number of the first subtype in the name of the double type (e.g. “page” in “pagenote”) is always displayed first. If you want to change this, set `\crfnmDefaultOrder` to `inverted` (defaults to `normal`). ### Specific values for given types {#specialize-config-macros} If you want to override some of these macros for a specific type, simply replace `Default` in its name with the (capitalized) name of the type (e.g. `\def\crfnmPageEnumDelim{; }`). Setting one of these macros to `\relax` will cause `\crossrefenum` to use the corresponding default macro instead. If you want a specific macro to be set to nothing, use an empty group (e.g. `\def\crfnmPageEnumDelim{}`). ## How to extend crossrefenum with other types and formats {#extending} Adding support for new types consists in defining the related macros in your preamble. Here is a commented example that would add support for references to lines in ConTeXt if this feature were not already included in _crossrefenum_. ```{.tex} % Register the types. Take care about capitalization! %% Set the names of the types and their corresponding macros: %% the control sequence should always be lowercase %% and the name of the type capitalized. \def\crfnm@line{Line} \def\crfnm@pageline{Pageline} %% Add them to the lists of known types. \crfnm@newListFrom[\crfnm@simpleRefTypes][\crfnm@line] -> \crfnm@simpleRefTypes \crfnm@newListFrom[\crfnm@doubleRefTypes][\crfnm@pageline] -> \crfnm@doubleRefTypes \crfnm@newListFrom[\crfnm@supportedTypes][\crfnm@line] -> \crfnm@supportedTypes \crfnm@newListFrom[\crfnm@supportedTypes][\crfnm@pageline] -> \crfnm@supportedTypes %% For the double types, set the primary and the secondary type. %% The primary type corresponds to the widest typographic unit %% (“page” for “pagenote”, “section” for “sectionpage”...). \let\crfnm@PagelinePrimary\crfnm@page \let\crfnm@PagelineSecondary\crfnm@line % Define the macro used to typeset the reference number. \def\crfnm@LineRef#1{\in[lr:b:#1]} % Define the macro used by \crossrefenum internally % to retrieve the raw reference number without typesetting it. % If you want to support multiple formats, % see how the macro \crfnm@case is used in crossrefenum.tex. % Note: the Lua function get_raw_ref_number is defined % in crossrefenum.tex for ConTeXt. \def\crfnm@getLineNumber#1{\directlua{get_raw_ref_number('lr:b:#1', 'linenumber')}} % Define all specific configuration options in the regular way. %% Required \def\crfnmLine{l.~} \def\crfnmLines{ll.~} %% If it differs from the defaults. \def\crfnmCollapsable{yes} \def\crfnmLineBeforeLastInSecond{, } \def\crfnmPagelineSubtypesSep{} \def\crfnmPagelinePrintFirstPrefix{once} \def\crfnmLineFormatInSecond#1{\crfnmSuperscript{#1}} \def\crfnmLinePrintPrefixInSecond{no} ``` Adding support to yet unsupported formats is far less trivial, not least because most of them do not provide a straightforward way to get reference numbers via purely expandable macros, which is required for _crossrefenum_ to perform its calculations. If you have wishes or hints about this, please contact me. ## Compatibility issues With LaTeX, the _nameref_ package (required by _hyperref_) must be loaded explicitely before _crossrefenum_. However, if the links in the output of `\crossrefenum` (not the numbers) point to a wrong location, you may wish to patch the macro to disable _hyperref_ locally: ``` {.tex} \makeatletter \let\oldcrfnm@enum\crfnm@enum \def\crfnm@enum[#1][#2]#3{\begin{NoHyper}\oldcrfnm@enum[#1][#2]{#3}\end{NoHyper}} \makeatother ```