diff options
author | Karl Berry <karl@freefriends.org> | 2022-11-14 20:17:23 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2022-11-14 20:17:23 +0000 |
commit | f7934ca125925655f6f228eb017ed474623acb80 (patch) | |
tree | c9452acc74cb0c5c7f94c3cfb360c42ac4d1117b /Master/texmf-dist/tex | |
parent | f1bc346e73a06c5be494cb6717d5e4a4f631613e (diff) |
crossrefenum (14nov22)
git-svn-id: svn://tug.org/texlive/trunk@65016 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex')
3 files changed, 1518 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/generic/crossrefenum/crossrefenum.sty b/Master/texmf-dist/tex/generic/crossrefenum/crossrefenum.sty new file mode 100644 index 00000000000..3d6ca15ae01 --- /dev/null +++ b/Master/texmf-dist/tex/generic/crossrefenum/crossrefenum.sty @@ -0,0 +1,3 @@ +\input crossrefenum.tex +\ProvidesPackage{crossrefenum}[\crfnmDate{} v\crfnmVersion{} Enumeration of cross-references] +\endinput
\ No newline at end of file diff --git a/Master/texmf-dist/tex/generic/crossrefenum/crossrefenum.tex b/Master/texmf-dist/tex/generic/crossrefenum/crossrefenum.tex new file mode 100644 index 00000000000..2b8d424a675 --- /dev/null +++ b/Master/texmf-dist/tex/generic/crossrefenum/crossrefenum.tex @@ -0,0 +1,1497 @@ +%*********************************************************************** +\def\crfnmName{crossrefenum} +\def\crfnmShortDesc{Smart typesetting of enumerated cross-references for various TeX formats} +\def\crfnmAuthor{Bastien Dumont} +\def\crfnmDate{2022/11/11} +\def\crfnmVersion{0.1} +% +% Copyright 2022 by Bastien Dumont (bastien.dumont@posteo.net) +% +% crossrefenum.tex is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% crossrefenum.tex is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with crossrefenum.tex. If not, see https://www.gnu.org/licenses/. +% +%*********************************************************************** + + + +% Terminology: +% – A simple reference is a reference by only one criterion (e.g. “page” or “note”). +% A double reference is a reference by two criteria (e.g. “page and note”), +% so has two subtypes: the primary and the secondary subtypes. +% Their labelling as primary and secondary is independant from their printed order : +% the primary subtype corresponds to the wider typographical unit, +% in which the secondary subtype is contained (so for “page and note”, +% the primary subtype is “page” and the secondary is “note”). +% – A single reference is a reference to one location (e.g. “p. 1”) +% A range is a reference to a span of text delimited by two single references (e.g. “pp. 1–5”). +% – An enumeration is a group containing a sequence of one or more references enclosed in groups. + +% Format-specific implementation notes: +% – In ConTeXt, the argument of \expanded cannot contain parameters: +% hence the ugly bridges of \expandafter that unfortunately cannot be +% replaced with a combination of \expanded and \noexpand. + +% How to add support for a new format: +% – Add a macro expanding to the name of the format at the beginning +% of the section “Initialization: Format-specific”; +% – Add a case in all blocks beginning with \crfnm@case[\fmtname] +% to setup the macros defined there with the required format-specific code. + + +% OUTLINE +% +% Initialization +% Catcodes +% Programming macros +% Format-specific +% Auxiliary file +% Constants +% Conditionals +% Auxiliary macros related to the data structure of \crossrefenum +% Default configuration +% +% \crossrefenum +% Public macro with optional arguments +% Main private macro +% Processing the individual references in the enumeration + +%%% Initialization: Catcodes %%% + +\newcount\crfnmOriginalCatcodeAt +% We can't write "crfnm@" here since the catcode +% of @ has not been redefined yet. +\crfnmOriginalCatcodeAt=\catcode`\@ +\catcode`\@=11 + +%%% Initialization: Programming macros %%% + +% \crfnm@case is a standard case statement. +% #1 is the string or the purely expandable macro to be tested. +% #2 is a sequence of tests of the form: +% value: token or group used if #1 is equal to value +% The sequence ends with \crfnm@endCases. +% In the groups to be executed, arguments in a macro definition +% have to be doubled. +% If all tests fails, does nothing and prints a warning on the terminal. + +\def\crfnm@case[#1] #2\crfnm@endCases{% + \begingroup + \edef\crfnm@comparandum{#1}% + \crfnm@@case #2% + \crfnm@comparandum: {% + \crfnm@warn{% + All tests failed in \unexpanded{\crfnm@case[#1] + #2 \crfnm@endCases}, doing nothing% + }% + } + \crfnm@endCases +} + +\def\crfnm@@case #1: #2{% + \edef\crfnm@comparans{#1}% + \ifx\crfnm@comparans\crfnm@comparandum + \def\crfnm@todo{\endgroup #2\crfnm@gobbleNextCases}% + \else + \def\crfnm@todo{\expandafter\crfnm@@case\crfnm@gobbleSpaces}% + \fi + \crfnm@todo +} + +\def\crfnm@gobbleSpaces#1{#1} + +\def\crfnm@gobbleNextCases #1\crfnm@endCases{} + +\def\crfnm@newCsnameAlias[#1]#2{% + % #1 is a control sequence (e.g. \mymacro). + % #2 is a cs name corresponding to an already defined + % control sequence (e.g. mappedto\tobereplaced). + \expandafter\let\expandafter#1\csname #2\endcsname +} + +\def\crfnm@capitalize#1{% + \expandafter\crfnm@uppercaseFirstLetter #1% +} + +\def\crfnm@uppercaseFirstLetter#1{% + % \uppercase, \lowercase and \crfnm@case + % are not purely expandable + \ifx#1aA% + \else\ifx#1bB% + \else\ifx#1cC% + \else\ifx#1dD% + \else\ifx#1eE% + \else\ifx#1fF% + \else\ifx#1gG% + \else\ifx#1hH% + \else\ifx#1iI% + \else\ifx#1jJ% + \else\ifx#1kK% + \else\ifx#1lL% + \else\ifx#1mM% + \else\ifx#1nN% + \else\ifx#1oO% + \else\ifx#1pP% + \else\ifx#1qQ% + \else\ifx#1rR% + \else\ifx#1sS% + \else\ifx#1tT% + \else\ifx#1uU% + \else\ifx#1vV% + \else\ifx#1wW% + \else\ifx#1xX% + \else\ifx#1yY% + \else\ifx#1zZ% + % In forks, the first argument of \crossrefenum is \crfnm@secondarySubtype, + % so it is already capitalized. + \else #1% + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi +} + +\def\crfnm@ifequal[#1][#2]#3#4{% + \edef\crfnm@comparans{#1}% + \edef\crfnm@comparandum{#2}% + \ifx\crfnm@comparans\crfnm@comparandum #3\else #4\fi +} + +\def\crfnm@loopOverArgs #1with #2{% + \crfnm@loopOver@args[#2]#1\crfnm@end +} + +\def\crfnm@loopOver@args[#1]#2{% + % #1 is the macro with one argument + % to be called with #2 + \edef\crfnm@arg{#2}% + \ifx\crfnm@arg\crfnm@end + \def\crfnm@todo{}% + \else + #1{#2}% + \def\crfnm@todo{\crfnm@loopOver@args[#1]}% + \fi + \crfnm@todo +} + +\def\crfnm@ifIsOneOf[#1][#2]#3#4{% + % #1 expands to a string, #2 expands to a list + \crfnm@foundfalse + \def\crfnm@setIfFound ##1{% + \edef\crfnm@itemSearched{#1}% + \edef\crfnm@toBeTested{##1}% + \ifx\crfnm@toBeTested\crfnm@itemSearched + \crfnm@foundtrue + \fi + }% + \expandafter\crfnm@loopOverArgs #2with \crfnm@setIfFound + \ifcrfnm@found + \def\crfnm@todo{#3}% + \else + \def\crfnm@todo{#4}% + \fi + \crfnm@todo +} + +%%% Initialization: Format-specific %%% + +% \fmtname changes between MKIV and LMTX in ConTeXt, +% so we use the value of \contextformat (to which \fmtname +% is made let-equal in ConTeXt). +% See https://source.contextgarden.net/tex/context/base/mkiv/context.mkiv?search=%5Cfmtname#l49 +\edef\crfnm@context{\csname contextformat\endcsname} +\def\crfnm@latex{LaTeX2e} +\def\crfnm@optex{OpTeX} + +% Supported types +\crfnm@case[\fmtname] + \crfnm@context: { + \def\crfnm@supportedTypes{{\crfnm@page}{\crfnm@note}{\crfnm@line}{\crfnm@pagenote}{\crfnm@pageline}} + } + \crfnm@latex: { + \def\crfnm@supportedTypes{{\crfnm@page}{\crfnm@note}{\crfnm@edpage}{\crfnm@edline}{\crfnm@pagenote}{\crfnm@edpageline}} + } +\crfnm@endCases + +% The following format-specific instructions are necessary to get +% the raw page number, which is used in comparisons. +% The raw page number must be unique (e.g. absolute page number). +% It must be possible to get it via a purely expandable macro. +\crfnm@case[\fmtname] + \crfnm@context: { + % Since I have not found any ConTeXt macro to get the raw values, + % we look directly in the auxiliary file from the second pass on. + \directlua{ + ordered_ref_data = structures.lists.collected + arbitrary_ref_data = structures.references.collected + function get_raw_ref_number(label, type) + found = false + if arbitrary_ref_data then + for _, ref_data in pairs(arbitrary_ref_data) do + if ref_data[label] then + label_data = ref_data[label] + for _, data_part in pairs(label_data) do + if data_part[type] then + found = true + tex.print(data_part[type]) + end + end + end + end + end + if not(found) and ordered_ref_data then + for i = 1, \luaescapestring{\utfchar{0x0023}ordered_ref_data} do + if ordered_ref_data[i].references.reference == label then + found = true + tex.print(ordered_ref_data[i].references[type]) + end + end + end + if not(found) then tex.print(0) end + end + } + } + \crfnm@latex: { + % If you use nameref (e.g. through hyperref), + % please make sure to load it before this code + % so that it does not erase our redefinition of \label. + % More details here: + % https://comp.text.tex.narkive.com/PI1P2Nlt/hyperref-and-redefining-label-ref-and-pageref-again + \RequirePackage[abspage]{zref} + \zref@setdefault{0} + \let\crfnm@label@beforezref\label + \def\label##1{% + \crfnm@label@beforezref{##1}% + \zref@labelbyprops{##1}{abspage, default}% + } + } +\crfnm@endCases + +% Macros for getting raw reference numbers. +% They must be purely expandable. +\crfnm@case[\fmtname] + \crfnm@context: { + \def\crfnm@getPageNumber##1{\directlua{get_raw_ref_number('##1', 'realpage')}} + \def\crfnm@getNoteNumber##1{\directlua{get_raw_ref_number('##1', 'order')}} + \def\crfnm@getLineNumber##1{\directlua{get_raw_ref_number('lr:b:##1', 'linenumber')}} + } + \crfnm@latex: { + \def\crfnm@getPageNumber##1{\zref@extract{##1}{abspage}} + \def\crfnm@getNoteNumber##1{\zref@extract{##1}{default}} + \def\crfnm@getEdpageNumber##1{\xpageref{##1}} + \def\crfnm@getEdlineNumber##1{\xlineref{##1}} + } +\crfnm@endCases + +% Macros for typesetting the references. +\crfnm@case[\fmtname] + \crfnm@context: { + \def\crfnm@PageRef##1{\at[##1]} + \def\crfnm@NoteRef##1{\in[##1]} + \def\crfnm@LineRef##1{\in[lr:b:##1]} + } + \crfnm@latex: { + \def\crfnm@PageRef##1{\pageref{##1}} + \def\crfnm@NoteRef##1{\ref{##1}} + \def\crfnm@EdpageRef##1{\edpageref{##1}} + \def\crfnm@EdlineRef##1{\edlineref{##1}} + } +\crfnm@endCases + +% Formatting macros +\crfnm@case[\fmtname] + \crfnm@context: { + \let\crfnmSuperscript\high + \let\crfnmSubscript\low + } + \crfnm@latex: { + \let\crfnmSuperscript\textsuperscript + \let\crfnmSubscript\textsubscript + } +\crfnm@endCases + +% Issue warnings +% \crfnm@warn@newPassNeeded is needed only for those format +% that may not reprocess the TeX file automatically +% when the auxiliary file changed. +\crfnm@case[\fmtname] + \crfnm@latex: { + \let\crfnm@warn@newPassNeeded\@latex@warning@no@line + } + \fmtname: {\let\crfnm@warn@newPassNeeded\relax} +\crfnm@endCases + + +%%% Initialization: Auxiliary file %%% + +% Configure the auxiliary file. +\crfnm@case[\fmtname] + \crfnm@context: { + \definedataset[printedRefsNb] + } + \crfnm@latex: { + \let\crfnm@auxfile\@auxout + } +\crfnm@endCases + +% Initialize the counter used in the auxiliary file +% to identify the informations associated with each +% call to \crossrefenum +\newcount\crfnm@ienum +\crfnm@ienum=0 +% In double references, we need to count separately +% the number of typeset references (after collapsing) +% for each part. To do this, we divide by 2 the maximum value +% that a counter can have and we use the result +% as the start of the index for \crfnm@ienum when used +% on the secondary subtype of a double reference. +% As a consequence, it is not possible to use \crossrefenum +% more than 2^30/2 times in the same document. +\newcount\crfnm@ienum@secondaryOfDouble +\crfnm@ienum@secondaryOfDouble=0 +\def\crfnm@secondaryOfDouble@istart{536870912} % = 2^30/2 +% This counter is used to register the total number +% of typeset references for every invocation of \crossrefenum +% (for a simple reference) or for each part of a double reference. +\newcount\crfnm@printedRefsNb +\crfnm@printedRefsNb=0 + +%%% Initialization: Constants %%% + +% Keywords and parameter values +\def\crfnm@empty{} +\def\crfnm@always{always} +\def\crfnm@yes{yes} +\def\crfnm@plural{pl} +\def\crfnm@first{first} +\def\crfnm@singleFirst{singlefirst} +\def\crfnm@end{crfnm@end} +\def\crfnm@labelRangeSep{ to } +\def\crfnm@withPrefix{withprefix} +\def\crfnm@enumend{crfnm@enumend} +\def\crfnm@normal{normal} +\def\crfnm@crossrefenum@secondArg@possibleValues{{withprefix}{noprefix}{yes}{no}} + +% Reference types +% Reledmac \pstartref is not supported, since users know if two consecutive references +% are in the same paragraph or not. They can alternate between direct use of \pstartref +% and \crossrefenum for lines and/or pages. +% \annotationref is not supported because I don't have any experience of it. +\def\crfnm@page{Page} +\def\crfnm@note{Note} +\def\crfnm@line{Line} +\def\crfnm@edpage{Edpage} +\def\crfnm@edline{Edline} +\def\crfnm@pagenote{Pagenote} +\def\crfnm@pageline{Pageline} +\def\crfnm@edpageline{Edpageline} +\let\crfnm@PagenotePrimary\crfnm@page +\let\crfnm@PagenoteSecondary\crfnm@note +\let\crfnm@PagelinePrimary\crfnm@page +\let\crfnm@PagelineSecondary\crfnm@line +\let\crfnm@EdpagelinePrimary\crfnm@edpage +\let\crfnm@EdpagelineSecondary\crfnm@edline + +%%% Initialization: Conditionals %%% + +\newif\ifcrfnm@found +\newif\ifcrfnm@enumIsFinished +\newif\ifcrfnm@simulated +\newif\ifcrfnm@areSingleAndRange +\newif\ifcrfnm@isFirstToken +\newif\ifcrfnm@singleFirst + + +%%% Initialization: Auxiliary macros related to the data structure of \crossrefenum %%% + +\edef\crfnm@simpleRefTypes{{\crfnm@page}{\crfnm@note}{\crfnm@line}{\crfnm@edpage}{\crfnm@edline}} +\edef\crfnm@doubleRefTypes{{\crfnm@pagenote}{\crfnm@pageline}{\crfnm@edpageline}} +\edef\crfnm@customizableDefaultConfig{{Collapsable}{EnumDelim}{EnumDelimInSecond}{BeforeLastInEnum}{BeforeLastInSecond}{RangeSep}} +\edef\crfnm@customizableDefaultDoubleConfig{{Collapsable}{EnumDelim}{BeforeLastInEnum}{RangeSep}{SubtypesSep}{PrintFirstPrefix}{GroupSubtypes}{Order}} +\edef\crfnm@customizableDefaultSecondaryOfDoubleConfig{{Collapsable}{NumberingContinuousAcrossDocument}{PrintPrefixInSecond}{FormatInSecond}} + +\newif\ifcrfnm@isDoubleRef +\def\crfnm@ifIsDoubleRef#1#2{\ifcrfnm@isDoubleRef #1\else #2\fi} + +\def\crfnm@ifIsRange#1#2#3{% + \expandafter\crfnm@ifIs\crfnm@labelRangeSep @in {#1} {#2} {#3}% +} + +\def\crfnm@ifIs#1@in #2#3#4{% + \def\crfnm@ifIsIn##1#1##2\@nil{% + \def\crfnm@afterSubstring{##2}% + \ifx\crfnm@afterSubstring\crfnm@empty #4\else #3\fi + }% + \expandafter\crfnm@ifIsIn#2#1\@nil +} + +\def\crfnm@enumid#1{% + \crfnm@ifIsSecondaryOfDouble[ienum: #1]{% + secondaryofdouble@% + \romannumeral\numexpr #1-\crfnm@secondaryOfDouble@istart\relax + @\romannumeral\the\crfnm@ienum@secondaryOfDouble + }{% + \romannumeral #1% + }% +} + +\def\crfnm@currEnumId{\crfnm@enumid{\the\crfnm@ienum}} + +\def\crfnm@ifIsSecondaryOfDouble[ienum: #1]#2#3{% + \ifnum #1 > \crfnm@secondaryOfDouble@istart + #2% + \else + #3% + \fi +} + +\def\crfnm@setIfIsDoubleRef{% + \crfnm@ifIsOneOf[\crfnm@refType][\crfnm@doubleRefTypes]{% + \crfnm@isDoubleReftrue + }{% + \crfnm@isDoubleReffalse + }% +} + +\def\crfnm@ifIsList[#1]#2#3{% + \expandafter\futurelet\expandafter\crfnm@nextToken + \expandafter\crfnm@ifIsBgroup #1\endofcheck{#2}{#3}% +} + +\def\crfnm@ifIsBgroup#1\endofcheck#2#3{% + % \crfnm@nextToken is the first token in the #1 of \crfnm@ifIsList. + % All the #1 of \crfnm@ifIsList is stored here in #1 and discarded. + \ifx\crfnm@nextToken\bgroup #2\else #3\fi +} + +\def\crfnm@newListFrom[#1][#2] -> #3{% + % #1 is either a list or a reference. + % #2 is a reference. + % #2 is appended to #1. + % #3 is the control sequence which the resulting list will be bound to. + \crfnm@ifIsList[#1]{% + \edef#3{#1{#2}}% + }{% + \edef#3{{#1}{#2}}% + }% +} + +\def\crfnm@replaceFirstInList[#1]#2{% + % #1 is a token, #2 is a list of tokens + {#1}\crfnm@gobbleFirst #2% +} + +\def\crfnm@gobbleFirst#1{} + + +%%% Initialization: Default configuration %%% + +% Prefixes +\def\crfnmPage{p.~} +\def\crfnmPages{pp.~} +\def\crfnmNote{n.~} +\def\crfnmNotes{nn.~} +\def\crfnmLine{l. } +\def\crfnmLines{ll.} +\let\crfnmEdpage\crfnmPage +\let\crfnmEdpages\crfnmPages +\def\crfnmEdline{l.~} +\def\crfnmEdlines{ll.~} + +% Macros with typed and default variants +\def\crfnmDefaultCollapsable{yes} +\def\crfnmNoteCollapsable{no} +\def\crfnmDefaultNumberingContinuousAcrossDocument{yes} +\def\crfnmDefaultEnumDelim{, } +\let\crfnmDefaultEnumDelimInSecond\crfnmDefaultEnumDelim +\def\crfnmDefaultBeforeLastInEnum{ and } +\let\crfnmDefaultBeforeLastInSecond\crfnmDefaultBeforeLastInEnum +\def\crfnmDefaultRangeSep{–} +\def\crfnmDefaultSubtypesSep{, } +\def\crfnmDefaultPrintFirstPrefix{always} +\def\crfnmDefaultFormatInSecond#1{#1} +\def\crfnmDefaultPrintPrefixInSecond{yes} +\def\crfnmDefaultGroupSubtypes{no} +\let\crfnmDefaultOrder\crfnm@normal + +%%% \crossrefenum %%% + +%%% \crossrefenum: Public macro with optional arguments %%% + +% \crossrefenum has two optional arguments. +% See the definition of \crfnm@enum below for the recognized values. + +\def\crfnm@firstArg@default{page} +\def\crfnm@secondArg@default{withprefix} + +\def\crossrefenum{% + \futurelet\crfnm@nextToken\crfnm@setEnumMacro +} + +\def\crfnm@setEnumMacro{% + \ifx\crfnm@nextToken [% + \def\crfnm@todo{\crfnm@setArgAndContinue[first]}% + \else + \def\crfnm@todo{% + \expandafter\expandafter\expandafter\crfnm@enum + \expandafter\expandafter\expandafter + % The following line break must be commented out. + [\expandafter\crfnm@firstArg@default\expandafter]% + \expandafter[\crfnm@secondArg@default]% + }% + \fi + \crfnm@todo +} + +\def\crfnm@setArgAndContinue[#1][#2]{% + % #1 is "first" or "second" + % #2 is one of the optional arguments + % passed to the main macro + \def\crfnm@argPos{#1}% + \def\crfnm@argValue{#2}% + \futurelet\crfnm@nextToken\crfnm@set@argAndContinue +} + +\def\crfnm@set@argAndContinue{% + \ifx\crfnm@argPos\crfnm@first + \ifx\crfnm@nextToken [% + \edef\crfnm@firstArg{[\crfnm@argValue]}% + \def\crfnm@todo{\crfnm@setArgAndContinue[second]}% + \else + \crfnm@ifIsOneOf[\crfnm@argValue][\crfnm@crossrefenum@secondArg@possibleValues]{% + \def\crfnm@todo{% + \crfnm@enum[\crfnm@firstArg@default][\crfnm@argValue]% + }% + }{% + \def\crfnm@todo{% + \crfnm@enum[\crfnm@argValue][\crfnm@secondArg@default]% + }% + }% + \fi + \else + \def\crfnm@todo{% + \expandafter\crfnm@enum\crfnm@firstArg[\crfnm@argValue]% + }% + \fi + \crfnm@todo +} + +%%% \crossrefenum: Main private macro %%% + +\def\crfnm@enum[#1][#2]#3{% + % #1 = reference type + % #2 = withprefix / noprefix or yes / no + % #3 = the enumeration + {% + % Initializes the environment for this invocation, + % then passes the enumeration to the parsing + % and formatting macro \crfnm@formatEnum. + \global\advance\crfnm@ienum by 1 + % The reference type is capitalized so that it can be used + % to refer to macro names typed in camelCase + % (e.g. in \crfnm@initializeCsnames). + \edef\crfnm@refType{\crfnm@capitalize{#1}}% + \crfnm@ifIsOneOf[\crfnm@refType][\crfnm@supportedTypes]{}{% + \errmessage{crossrefenum: Unsupported type + #1 for format \fmtname{}.} + }% + \crfnm@setIfIsDoubleRef + \crfnm@applyDefaultConfigIfUndefined + \crfnm@initializeCsnames + \edef\crfnm@hasPrefix{#2}% + \ifx\crfnm@hasPrefix\crfnm@withPrefix + \let\crfnm@hasPrefix\crfnm@yes + \fi + \crfnm@enumIsFinishedfalse + \crfnm@isFirstTokentrue + \crfnm@ifIsSecondaryOfDouble[ienum: \the\crfnm@ienum]{% + \global\advance\crfnm@ienum@secondaryOfDouble by 1 + }{}% + % We get the number of references typeset for the current + % invocation of \crossrefenum in the last compilation to know + % whether to use the singular or plural form of the prefix. + \edef\crfnm@printedRefsNb@previousPass{% + \crfnm@getPrintedRefsNb@previousPass + }% + % The following macro will process sequentially + % all references in the enumeration. + \expandafter\crfnm@formatEnum#3{crfnm@enumend}% + }% +} + +\def\crfnm@applyDefaultConfigIfUndefined{% + \def\crfnm@applyToThisType{\crfnm@applyDefaultMacroToType[\crfnm@refType]}% + \crfnm@ifIsDoubleRef{% + \expandafter\crfnm@loopOverArgs \crfnm@customizableDefaultDoubleConfig with \crfnm@applyToThisType + \crfnm@setSubtypesOrder + \def\crfnm@applyToPrimarySubtype{\crfnm@applyDefaultMacroToType[\csname crfnm@\crfnm@refType Primary\endcsname]}% + \expandafter\crfnm@loopOverArgs \crfnm@customizableDefaultConfig with \crfnm@applyToPrimarySubtype + \def\crfnm@applyToSecondarySubtype{\crfnm@applyDefaultMacroToType[\csname crfnm@\crfnm@refType Secondary\endcsname]}% + \expandafter\crfnm@loopOverArgs \crfnm@customizableDefaultSecondaryOfDoubleConfig with \crfnm@applyToSecondarySubtype + }{% + \expandafter\crfnm@loopOverArgs \crfnm@customizableDefaultConfig with \crfnm@applyToThisType + }% +} + +\def\crfnm@applyDefaultMacroToType[#1]#2{% + % #1 = type, #2 = csname without "crfnmDefault" + % \csname crfnm#1#2\endcsname is the csname for this type + \expandafter\ifx\csname crfnm#1#2\endcsname\relax + % The csname must be generated before it is passed + % to \let in \crfnm@newCsnameAlias + \expandafter\crfnm@newCsnameAlias\expandafter[\csname crfnm#1#2\endcsname] + {crfnmDefault#2}% + \fi +} + +\def\crfnm@initializeCsnames{% + \crfnm@newCsnameAlias[\crfnm@rangeSep]{crfnm\crfnm@refType RangeSep}% + \crfnm@ifIsDoubleRef{% + \crfnm@newCsnameAlias[\crfnm@doubleRefOrder]{crfnm\crfnm@refType Order}% + \crfnm@newCsnameAlias[\crfnm@firstSubtype]{crfnm@\crfnm@refType First}% + \crfnm@newCsnameAlias[\crfnm@secondSubtype]{crfnm@\crfnm@refType Second}% + \crfnm@newCsnameAlias[\crfnm@primarySubtype]{crfnm@\crfnm@refType Primary}% + \crfnm@newCsnameAlias[\crfnm@secondarySubtype]{crfnm@\crfnm@refType Secondary}% + \crfnm@newCsnameAlias[\crfnm@getRawValuePrimary]{crfnm@get\crfnm@primarySubtype Number}% + \crfnm@newCsnameAlias[\crfnm@getRawValueSecondary]{crfnm@get\crfnm@secondarySubtype Number}% + \crfnm@newCsnameAlias[\crfnm@primaryCollapsable]{crfnm\crfnm@primarySubtype Collapsable}% + \crfnm@newCsnameAlias[\crfnm@secondaryCollapsable]{crfnm\crfnm@secondarySubtype Collapsable}% + \crfnm@newCsnameAlias[\crfnm@secondaryNumberingContinuous]{crfnm\crfnm@secondarySubtype NumberingContinuousAcrossDocument}% + \crfnm@newCsnameAlias[\crfnm@enumDelim]{crfnm\crfnm@refType EnumDelim}% + \crfnm@newCsnameAlias[\crfnm@beforeLastInEnum]{crfnm\crfnm@refType BeforeLastInEnum}% + \crfnm@newCsnameAlias[\crfnm@separatorBetweenSubtypes]{crfnm\crfnm@refType SubtypesSep}% + \crfnm@newCsnameAlias[\crfnm@formatSecondary]{crfnm\crfnm@secondarySubtype FormatInSecond}% + \crfnm@newCsnameAlias[\crfnm@printFirstPrefix]{crfnm\crfnm@refType PrintFirstPrefix}% + \crfnm@newCsnameAlias[\crfnm@isSecondaryPrefixPrinted]{crfnm\crfnm@secondarySubtype PrintPrefixInSecond}% + \crfnm@newCsnameAlias[\crfnm@groupSubtypes]{crfnm\crfnm@refType GroupSubtypes}% + }{% + \crfnm@ifIsSecondaryOfDouble[ienum: \the\crfnm@ienum]{% + \crfnm@newCsnameAlias[\crfnm@enumDelim]{crfnm\crfnm@refType EnumDelimInSecond}% + \crfnm@newCsnameAlias[\crfnm@beforeLastInEnum]{crfnm\crfnm@refType BeforeLastInSecond}% + }{% + \crfnm@newCsnameAlias[\crfnm@enumDelim]{crfnm\crfnm@refType EnumDelim}% + \crfnm@newCsnameAlias[\crfnm@beforeLastInEnum]{crfnm\crfnm@refType BeforeLastInEnum}% + }% + \crfnm@newCsnameAlias[\crfnm@collapsable]{crfnm\crfnm@refType Collapsable}% + \crfnm@newCsnameAlias[\crfnm@getRawValue]{crfnm@get\crfnm@refType Number}% + \crfnm@newCsnameAlias[\crfnm@typesetSingleRef]{crfnm@\crfnm@refType Ref}% + }% +} + +\def\crfnm@setSubtypesOrder{% + \crfnm@newCsnameAlias[\crfnm@thisTypePrimary]{crfnm@\crfnm@refType Primary}% + \crfnm@newCsnameAlias[\crfnm@thisTypePrimary]{crfnm@\crfnm@refType Primary}% + \expandafter\ifx\csname crfnm\crfnm@refType Order\endcsname\crfnm@normal + \expandafter\let\csname crfnm@\crfnm@refType First\endcsname% + \crfnm@thisTypePrimary + \expandafter\let\csname crfnm@\crfnm@refType Second\endcsname% + \crfnm@thisTypeSecondary + \else + \expandafter\let\csname crfnm@\crfnm@refType First\endcsname% + \crfnm@thisTypeSecondary + \expandafter\let\csname crfnm@\crfnm@refType Second\endcsname% + \crfnm@thisTypePrimary + \fi +} + +\def\crfnm@ifIsInverted#1#2{% + \ifx\crfnm@doubleRefOrder\crfnm@normal #2\else #1\fi +} + +% Get the number of the parts of the current enumeration +% in the preceding pass from the auxiliary file. +% The macro must be purely expandable and return a number. +\crfnm@case[\fmtname] + \crfnm@context: { + \def\crfnm@getPrintedRefsNb@previousPass{% + \directlua{ + registeredValue = '\datasetvariable{printedRefsNb}{\crfnm@currEnumId}{value}' + if registeredValue == '' then tex.print(0) else tex.print(registeredValue) end + }% + } + } + \fmtname: { + \def\crfnm@getPrintedRefsNb@previousPass{% + \expandafter + \ifx\csname crfnm@printedrefsnb@\crfnm@currEnumId\endcsname\relax + 0 + \else + \csname crfnm@printedrefsnb@\crfnm@currEnumId\endcsname + \fi + } + } +\crfnm@endCases + +%%% \crossrefenum: Processing the individual references in the enumeration %%% + +\def\crfnm@formatEnum#1{% + % #1 is a string consisting of either: + % * <label> + % * <label1> to <label2> + % * crfnm@enumend + \crfnm@ifIsBeginOfEnum{% + \crfnm@setCurrentRef{#1}% + % We typeset the prefix at the beginning of the enumeration + % for simple references for it appears once at the beginning of the enumeration. + % For double references, it is typeset at the beginning + % of every part of the enumeration. + \crfnm@ifIsDoubleRef{}{\crfnm@typesetPrefix}% + }{% + \crfnm@triggerWarnings{#1}% + \crfnm@advanceInEnumWith{#1}% + % The following macro compares the current reference + % with the preceding one and either merges them + % or typesets the preceding reference. + \crfnm@combine + }% + \crfnm@ifIsEndOfEnum{% + \ifnum\crfnm@printedRefsNb@previousPass=\the\crfnm@printedRefsNb\relax\else + \crfnm@warn@newPassNeeded{% + crossrefenum changed some enumerations. + Rerun to get all prefixes right.% + }% + \fi + \crfnm@registerPrintedRefsNb + \crfnm@ifIsDoubleRef{\global\crfnm@ienum@secondaryOfDouble=0}{}% + }{% + \expandafter\crfnm@formatEnum + }% +} + +\def\crfnm@setCurrentRef#1{% + \crfnm@ifIsDoubleRef{% + \def\crfnm@currentPrimary{#1}% + \def\crfnm@currentSecondary{#1}% + }{% + \def\crfnm@current{#1}% + }% +} + +\def\crfnm@ifIsBeginOfEnum#1#2{% + \edef\crfnm@csnameCurrent{% + crfnm@current\ifcrfnm@isDoubleRef Primary\fi% + }% + \expandafter\ifx\csname\crfnm@csnameCurrent\endcsname\relax + #1% + \else + #2% + \fi +} + +\def\crfnm@typesetPrefix{% + \ifx\crfnm@hasPrefix\crfnm@yes + \crfnm@ifIsDoubleRef{% + \crfnm@ifIsInverted{% + \crfnm@typeset@@prefix[sg]% + }{% + \ifx\crfnm@printFirstPrefix\crfnm@always + \crfnm@typeset@@prefix[sg]% + \else + \ifcrfnm@isFirstToken\crfnm@typeset@prefix\fi + \fi + }% + }{% + \crfnm@typeset@prefix + }% + \fi +} + +\def\crfnm@typeset@prefix{% + \ifnum\crfnm@printedRefsNb@previousPass>1 + \crfnm@typeset@@prefix[pl]% + \else + \crfnm@typeset@@prefix[sg]% + \fi +} + +\def\crfnm@typeset@@prefix[#1]{% + \def\crfnm@prefixform{#1}% + \csname crfnm% + \crfnm@ifIsDoubleRef{% + \crfnm@ifIsInverted{\crfnm@secondSubtype}{\crfnm@firstSubtype}% + }{% + \crfnm@refType + }% + \ifx\crfnm@prefixform\crfnm@plural s\fi + \endcsname +} + +\def\crfnm@triggerWarnings#1{% + % Raises the “undefined label” or “references have changed” warnings + % even if the label doesn't get used in this pass, thus causing a new + % pass to be performed. + % Works in LaTeX because warnings are sent via \immediate\write. + % It should also work in ConTeXt because it writes the logs through + % a Lua call, not \write. + \def\crfnm@tested{#1}% + \ifx\crfnm@tested\crfnm@enumend\else + \setbox0=\hbox{\crfnm@simulateTypesetting{#1}}% + \fi +} + +\def\crfnm@simulateTypesetting#1{% + \crfnm@simulatedtrue + \crfnm@ifIsDoubleRef{% + % We can't use \crfnm@typesetdouble here, for it would result + % in nested calls to \setbox0. + % Nevertheless we have to test for both subtypes, + % since the value of each of them may change + % while that of the other remains the same. + \let\crfnm@realRefType\crfnm@refType% + \crfnm@isDoubleReffalse + \edef\crfnm@refType{\crfnm@primarySubtype}% + \crfnm@initializeCsnames + \crfnm@wrapInDisplayMacro{#1}% + \edef\crfnm@refType{\crfnm@secondarySubtype}% + \crfnm@initializeCsnames + \crfnm@wrapInDisplayMacro{#1}% + \let\crfnm@refType\crfnm@realRefType + \crfnm@isDoubleReftrue + \crfnm@initializeCsnames + }{% + \crfnm@wrapInDisplayMacro{#1}% + }% + \crfnm@simulatedfalse +} + +\def\crfnm@advanceInEnumWith#1{% + \crfnm@ifIsDoubleRef{% + \let\crfnm@precedingPrimary\crfnm@currentPrimary + \let\crfnm@precedingSecondary\crfnm@currentSecondary + \def\crfnm@currentPrimary{#1}% + \def\crfnm@currentSecondary{#1}% + }{% + \let\crfnm@preceding\crfnm@current + \def\crfnm@current{#1}% + }% +} + +\def\crfnm@combine{% + \crfnm@ifIsEndOfEnum{% + \crfnm@enumIsFinishedtrue + \ifcrfnm@isFirstToken\else\crfnm@beforeLastInEnum\fi + \crfnm@typesetPrecedingRef + }{% + \crfnm@compareTypes + \ifcrfnm@areSingleAndRange + \crfnm@combineSingleAndRange + \else + \edef\crfnm@maybeRange{\csname crfnm@current\crfnm@ifIsDoubleRef{Primary}{}\endcsname}% + \crfnm@ifIsRange\crfnm@maybeRange{% + \crfnm@combineRanges + }{% + \crfnm@combineSingles + }% + \fi + }% +} + +\def\crfnm@ifIsEndOfEnum#1#2{% + \edef\crfnm@currentInEnum{% + \crfnm@ifIsDoubleRef{% + \crfnm@currentPrimary + }{% + \crfnm@current + }% + }% + \ifx\crfnm@currentInEnum\crfnm@enumend + #1% + \else + #2% + \fi +} + +% Write the number of the parts of the current enumeration +% to the auxiliary file. +\def\crfnm@registerPrintedRefsNb{% + \crfnm@case[\fmtname] + \crfnm@context: {% + \ifx\fmtname\crfnm@context + \setdataset[printedRefsNb][\crfnm@currEnumId][value={\the\crfnm@printedRefsNb}]% + \fi + } + \fmtname: {% + \immediate\write\crfnm@auxfile{% + \gdef\expandafter\noexpand\csname crfnm@printedrefsnb@\crfnm@currEnumId\endcsname + {\the\crfnm@printedRefsNb}% + }% + } + \crfnm@endCases +} + +\def\crfnm@typesetPrecedingRef{% + \crfnm@ifIsDoubleRef{% + \crfnm@typesetDoubleRef{\crfnm@precedingPrimary}{\crfnm@precedingSecondary}% + }{% + \crfnm@wrapInDisplayMacro{\crfnm@preceding}% + }% +} + +\def\crfnm@compareTypes{% + \crfnm@setPossibleRangeCs + \crfnm@ifIsRange{\crfnm@possibleRange@preceding}{% + \crfnm@ifIsRange{\crfnm@possibleRange@current}{% + \crfnm@areSingleAndRangefalse + }{% + \crfnm@areSingleAndRangetrue + }% + }{% + \crfnm@ifIsRange{\crfnm@possibleRange@current}{% + \crfnm@areSingleAndRangetrue + }{% + \crfnm@areSingleAndRangefalse + }% + }% +} + +\def\crfnm@setPossibleRangeCs{% + % We use the secondary subtype, since it can become a range + % as an effect of \crfnm@combineSingles while the primary subtype + % keeps being a single; the reverse can't be true. + \crfnm@newCsnameAlias[\crfnm@possibleRange@preceding]{crfnm@preceding\ifcrfnm@isDoubleRef Secondary\fi}% + \crfnm@newCsnameAlias[\crfnm@possibleRange@current]{crfnm@current\ifcrfnm@isDoubleRef Secondary\fi}% +} + +\def\crfnm@combineSingles{% + \crfnm@ifIsDoubleRef{% + \crfnm@combine@singles@double + }{% + \crfnm@combine@singles@simple + }% +} + +\def\crfnm@combine@singles@double{% + \edef\crfnm@raw@precedingPrimary{\crfnm@getRawValuePrimary\crfnm@precedingPrimary}% + \edef\crfnm@raw@currentPrimary{\crfnm@getRawValuePrimary\crfnm@currentPrimary}% + \crfnm@ifequal[\crfnm@raw@precedingPrimary][\crfnm@raw@currentPrimary]{% + \edef\crfnm@currentPrimary{\crfnm@precedingPrimary}% + \crfnm@newListFrom[\crfnm@precedingSecondary][\crfnm@currentSecondary] -> \crfnm@currentSecondary + }{% + \crfnm@typesetPrecedingRange + }% +} + +\def\crfnm@combine@singles@simple{% + \edef\crfnm@raw@preceding{\crfnm@getRawValue\crfnm@preceding}% + \edef\crfnm@raw@current{\crfnm@getRawValue\crfnm@current}% + \crfnm@ifequal[\crfnm@raw@preceding][\crfnm@raw@current]{% + %Do nothing, so discard \crfnm@preceding. + }{% + \crfnm@ifConsecutiveCollapsable[\crfnm@raw@preceding][\crfnm@raw@current]{% + \edef\crfnm@current{\crfnm@preceding\crfnm@labelRangeSep\crfnm@current}% + }{% + \ifcrfnm@isFirstToken\else + \crfnm@enumDelim + \fi + \crfnm@wrapInDisplayMacro{\crfnm@preceding}% + }% + }% +} + +\def\crfnm@combineRanges{% + \crfnm@ifIsDoubleRef{% + \crfnm@combine@ranges@doubles + }{% + \crfnm@combine@ranges@simples + }% +} + +\def\crfnm@getLabelInRange@begin[#1]{% + \expandafter\crfnm@get@labelInRange@begin\expandafter[#1]% +} +\def\crfnm@getLabelInRange@end[#1]{% + \expandafter\crfnm@get@labelInRange@end\expandafter[#1]% +} +\expandafter\def\expandafter\crfnm@get@labelInRange@begin\expandafter[\expandafter#\expandafter1\crfnm@labelRangeSep#2]{#1}% +\expandafter\def\expandafter\crfnm@get@labelInRange@end\expandafter[\expandafter#\expandafter1\crfnm@labelRangeSep#2]{#2}% + +\def\crfnm@combine@ranges@simples{% + \edef\crfnm@precedingBegin{\crfnm@getLabelInRange@begin[\crfnm@preceding]}% + \edef\crfnm@precedingEnd{\crfnm@getLabelInRange@end[\crfnm@preceding]}% + \edef\crfnm@currentBegin{\crfnm@getLabelInRange@begin[\crfnm@current]}% + \edef\crfnm@currentEnd{\crfnm@getLabelInRange@end[\crfnm@current]}% + \edef\crfnm@raw@precedingBegin{\crfnm@getRawValue\crfnm@precedingBegin}% + \edef\crfnm@raw@precedingEnd{\crfnm@getRawValue\crfnm@precedingEnd}% + \edef\crfnm@raw@currentBegin{\crfnm@getRawValue\crfnm@currentBegin}% + \edef\crfnm@raw@currentEnd{\crfnm@getRawValue\crfnm@currentEnd}% + \def\crfnm@mergeRanges{% + \edef\crfnm@current{\crfnm@precedingBegin\crfnm@labelRangeSep\crfnm@currentEnd}% + }% + \crfnm@ifequal[\crfnm@raw@precedingEnd][\crfnm@raw@currentBegin]{% + \crfnm@mergeRanges + }{% + \crfnm@ifConsecutiveCollapsable[\crfnm@raw@precedingEnd][\crfnm@raw@currentBegin]{% + \crfnm@mergeRanges + }{% + \ifcrfnm@isFirstToken\else\crfnm@enumDelim\fi + \crfnm@wrapInDisplayMacro{\crfnm@preceding}% + }% + }% +} + +\def\crfnm@combine@ranges@doubles{% + % \crfnm@<pre/cur>Secondary are identical with \crfnm@<pre/cur>Primary + % at the beginning and at the end of this macro + % because the secondary value may not be an enumeration + % at either ends of a range. + % That is why we don't use them in our comparisons here. + % Note: when the lineation is not continuous, we cannot handle + % properly the case where the end of the first range is on the last + % line of a page and the beginning of the second range is on the + % first line of the following page. This is because we cannot know + % if a given line is the last on the page. + \edef\crfnm@precedingBegin{\crfnm@getLabelInRange@begin[\crfnm@precedingPrimary]}% + \edef\crfnm@precedingEnd{\crfnm@getLabelInRange@end[\crfnm@precedingPrimary]}% + \edef\crfnm@currentBegin{\crfnm@getLabelInRange@begin[\crfnm@currentPrimary]}% + \edef\crfnm@currentEnd{\crfnm@getLabelInRange@end[\crfnm@currentPrimary]}% + \edef\crfnm@primarySubtype@precedingEnd{\crfnm@getRawValuePrimary\crfnm@precedingEnd}% + \edef\crfnm@primarySubtype@currentBegin{\crfnm@getRawValuePrimary\crfnm@currentBegin}% + \edef\crfnm@secondarySubtype@precedingEnd{\crfnm@getRawValueSecondary\crfnm@precedingEnd}% + \edef\crfnm@secondarySubtype@currentBegin{\crfnm@getRawValueSecondary\crfnm@currentBegin}% + \crfnm@ifequal[\crfnm@primarySubtype@precedingEnd][\crfnm@primarySubtype@currentBegin]{% + \crfnm@ifequal[\crfnm@secondarySubtype@precedingEnd][\crfnm@secondarySubtype@currentBegin]{% + \crfnm@mergeRanges + }{% + \crfnm@ifConsecutiveCollapsable[secondary]% + [\crfnm@secondarySubtype@precedingEnd][\crfnm@secondarySubtype@currentBegin] + {% + \crfnm@mergeRanges + }{% + \edef\crfnm@primarySubtype@precedingBegin{\crfnm@getRawValuePrimary\crfnm@precedingBegin}% + \edef\crfnm@primarySubtype@currentEnd{\crfnm@getRawValuePrimary\crfnm@currentEnd}% + \crfnm@ifequal[\crfnm@primarySubtype@precedingBegin][\crfnm@primarySubtype@currentEnd]{% + % Two discountinuous ranges of the secondary subtype on the same page. + \crfnm@newListFrom[\crfnm@precedingSecondary][\crfnm@currentSecondary] -> \crfnm@currentSecondary + }{% + \crfnm@typesetPrecedingRange + }% + }% + }% + }{% + \ifx\crfnm@secondaryNumberingContinuous\crfnm@yes + % It would make no sense to test for identical line numbers here. + \crfnm@ifConsecutiveCollapsable[primary]% + [\crfnm@primarySubtype@precedingEnd][\crfnm@primarySubtype@currentBegin] + {% + \crfnm@ifConsecutiveCollapsable[secondary]% + [\crfnm@secondarySubtype@precedingEnd][\crfnm@secondarySubtype@currentBegin] + {% + \crfnm@mergeRanges + }{% + \crfnm@typesetPrecedingRange + }% + }{% + \crfnm@typesetPrecedingRange + }% + \else + \crfnm@typesetPrecedingRange + \fi + }% +} + +\def\crfnm@mergeRanges{% + \edef\crfnm@currentPrimary{\crfnm@precedingBegin\crfnm@labelRangeSep\crfnm@currentEnd}% + \let\crfnm@currentSecondary\crfnm@currentPrimary +} + +\def\crfnm@combineSingleAndRange{% + \crfnm@setPossibleRangeCs + \crfnm@ifIsRange{\crfnm@possibleRange@current}{% + \crfnm@combine@singleAndRange[singlefirst]% + }{% + \crfnm@combine@singleAndRange[reversed]% + }% +} + +\def\crfnm@typesetPrecedingRange{% + \ifcrfnm@isFirstToken\else + \crfnm@enumDelim + \fi + \crfnm@typesetDoubleRef{\crfnm@precedingPrimary}{\crfnm@precedingSecondary}% +}% + +\def\crfnm@combine@singleAndRange[#1]{% + \crfnm@setIfIsSingleFirst[#1]% + \crfnm@ifIsDoubleRef{% + \crfnm@combine@single@and@range@double + }{% + \ifcrfnm@singleFirst + \let\crfnm@single\crfnm@preceding + \let\crfnm@range\crfnm@current + \else + \let\crfnm@single\crfnm@current + \let\crfnm@range\crfnm@preceding + \fi + \crfnm@combine@single@and@range@simple + }% +} + +\def\crfnm@setIfIsSingleFirst[#1]{% + \def\crfnm@singlePos{#1}% expected: singlefirst or reversed + \ifx\crfnm@singlePos\crfnm@singleFirst + \crfnm@singleFirsttrue + \else + \crfnm@singleFirstfalse + \fi +} + +\def\crfnm@combine@single@and@range@simple{% + \edef\crfnm@begin@range{\crfnm@getLabelInRange@begin[\crfnm@range]}% + \edef\crfnm@end@range{\crfnm@getLabelInRange@end[\crfnm@range]}% + \edef\crfnm@raw@single{\crfnm@getRawValue\crfnm@single}% + \edef\crfnm@raw@begin@range{\crfnm@getRawValue\crfnm@begin@range}% + \edef\crfnm@raw@end@range{\crfnm@getRawValue\crfnm@end@range}% + \ifcrfnm@singleFirst + \crfnm@ifAreEqualOrConsecutiveCollapsable[][\crfnm@raw@single][\crfnm@raw@begin@range]{% + \edef\crfnm@current{% + \crfnm@newRangeWithReplacement[change: \crfnm@current, with: \crfnm@preceding, at: beg]% + }% + }{% + \crfnm@typesetInEnum{\crfnm@preceding}% + }% + \else + \crfnm@ifequal[\crfnm@raw@end@range][\crfnm@raw@single]{% + \edef\crfnm@current{\crfnm@preceding}% + }{% + \crfnm@ifConsecutiveCollapsable[\crfnm@raw@end@range][\crfnm@raw@single]{% + \edef\crfnm@current{% + \crfnm@newRangeWithReplacement[change: \crfnm@preceding, with: \crfnm@current, at: end]% + }% + }{% + \crfnm@typesetInEnum{\crfnm@preceding}% + }% + }% + \fi +} + +\def\crfnm@combine@single@and@range@double{% + \ifcrfnm@singleFirst + \edef\crfnm@currentBegin{\crfnm@getLabelInRange@begin[\crfnm@currentPrimary]}% + \edef\crfnm@primaryRawValue@preceding{\crfnm@getRawValuePrimary\crfnm@precedingPrimary}% + \edef\crfnm@primaryRawValue@current{\crfnm@getRawValuePrimary\crfnm@currentBegin}% + \edef\crfnm@secondaryRawValue@preceding{\crfnm@getRawValueSecondary\crfnm@precedingSecondary}% + \edef\crfnm@secondaryRawValue@current{\crfnm@getRawValueSecondary\crfnm@currentBegin}% + \let\crfnm@typesetPreceding\crfnm@typesetPrecedingRef + \def\crfnm@rangeRoot{crfnm@current}% + \def\crfnm@singleRoot{crfnm@preceding}% + \else + \edef\crfnm@precedingEnd{\crfnm@getLabelInRange@end[\crfnm@precedingPrimary]}% + \edef\crfnm@primaryRawValue@preceding{\crfnm@getRawValuePrimary\crfnm@precedingEnd}% + \edef\crfnm@primaryRawValue@current{\crfnm@getRawValuePrimary\crfnm@currentPrimary}% + \edef\crfnm@secondaryRawValue@preceding{\crfnm@getRawValueSecondary\crfnm@precedingEnd}% + \edef\crfnm@secondaryRawValue@current{\crfnm@getRawValueSecondary\crfnm@currentSecondary}% + \let\crfnm@typesetPreceding\crfnm@typesetPrecedingRange + \def\crfnm@rangeRoot{crfnm@preceding}% + \def\crfnm@singleRoot{crfnm@current}% + \fi + \crfnm@ifequal[\crfnm@primaryRawValue@current][\crfnm@primaryRawValue@preceding]{% + \crfnm@ifAreEqualOrConsecutiveCollapsable[secondary]% + [\crfnm@secondaryRawValue@current][\crfnm@secondaryRawValue@preceding]% + {% + \crfnm@mergeSingleAndRangeDouble + }{% + \crfnm@typesetPreceding + }% + }{% + \crfnm@ifConsecutiveCollapsable[secondary]% + [\crfnm@secondaryRawValue@current][\crfnm@secondaryRawValue@preceding]% + {% + \crfnm@mergeSingleAndRangeDouble + }{% + \crfnm@typesetPreceding + }% + }% +} + +\def\crfnm@mergeSingleAndRangeDouble{% + \edef\crfnm@changedBoundary{\ifcrfnm@singleFirst beg\else end\fi}% + \crfnm@mergeSingleAndRangeDouble@subtype + [changeRoot: \crfnm@rangeRoot, withRoot: \crfnm@singleRoot, at: \crfnm@changedBoundary][Primary]% + \crfnm@mergeSingleAndRangeDouble@subtype + [changeRoot: \crfnm@rangeRoot, withRoot: \crfnm@singleRoot, at: \crfnm@changedBoundary][Secondary]% +} + +\def\crfnm@mergeSingleAndRangeDouble@subtype[changeRoot: #1, withRoot: #2, at: #3][#4]{% + \edef\crfnm@rangeToBeChanged{\csname #1#4\endcsname}% + \edef\crfnm@singleForChange{\csname #2#4\endcsname}% + \expandafter\edef\csname crfnm@current#4\endcsname{% + \crfnm@newRangeWithReplacement + [change: \crfnm@rangeToBeChanged, with: \crfnm@singleForChange, at: #3]% + }% +} + +\def\crfnm@ifAreEqualOrConsecutiveCollapsable[#1][#2][#3]#4#5{% + % #1 is “primary”, “secondary” or empty (for simple types). + \crfnm@ifequal[#2][#3]{#4}{% + \crfnm@ifConsecutiveCollapsable[#1][#2][#3]{#4}{#5}% + }% +} + +\def\crfnm@ifConsecutiveCollapsable[#1]{% + % If the current reference has a double type, this macro must carry a first argument + % indicating if the current subtype is “primary”, “secondary”. + % With a simple type, it may be missing or empty. + % The other two arguments are the raw reference values to be compared. + % The comparison itself is performed by \crfnm@if@consecutiveCollapsable, + % which takes the type indication as a mandatory argument. + % The following code here simply sets the type if it is not provided by the user. + \crfnm@ifIsOneOf[#1][{{primary}{secondary}{}}]{% + \def\crfnm@macroWithParam{\crfnm@if@consecutiveCollapsable[#1]}% + }{% + \def\crfnm@macroWithParam{\crfnm@if@consecutiveCollapsable[][#1]}% + }% + \crfnm@macroWithParam +} + +\def\crfnm@if@consecutiveCollapsable[#1][#2][#3]#4#5{% + % #1 is “primary”, “secondary” or empty (for simple types). + % #2 and #3 are the raw numbers for the first and the second references. + \crfnm@newCsnameAlias[\crfnm@thisTypeCollapsable]{crfnm@\crfnm@ifIsDoubleRef{#1C}{c}ollapsable}% + \crfnm@newCsnameAlias[\crfnm@testedType]{crfnm@\crfnm@ifIsDoubleRef{#1Subtype}{refType}}% + \ifx\crfnm@thisTypeCollapsable\crfnm@yes + \crfnm@ifSimpleOrPrimaryType{% Uses \crfnm@testedType + \crfnm@ifAreConsecutive[#2][#3]{#4}{#5}% + }{% + \ifx\crfnm@secondaryNumberingContinuous\crfnm@yes + \crfnm@ifAreConsecutive[#2][#3]{#4}{#5}% + \else #5\fi + }% + \else #5\fi +} + +\def\crfnm@ifSimpleOrPrimaryType#1#2{% + \crfnm@ifIsOneOf[\crfnm@testedType][\crfnm@simpleRefTypes]{#1}{% + \ifx\crfnm@testedType\crfnm@primarySubtype #1\else #2\fi + }% +} + +\def\crfnm@ifAreConsecutive[#1][#2]#3#4{% + \ifnum\numexpr#1+1\relax=#2 #3\else #4\fi +} + +\def\crfnm@newRangeWithReplacement[change: #1, with: #2, at: #3#4]{% + % #3#4 is “beg” or “end” (we test only the first letter). + % This macro must be purely expandable. + \ifx #3b% + #2 to \crfnm@getLabelInRange@end[#1]% + \else + \crfnm@getLabelInRange@begin[#1] to #2% + \fi +} + +\def\crfnm@typesetInEnum#1{% + \ifcrfnm@isFirstToken\else\crfnm@enumDelim\fi + \crfnm@wrapInDisplayMacro{#1}% +} + +\def\crfnm@wrapInDisplayMacro#1{% + \crfnm@countInPrintedRefs + \crfnm@wrapRangeOrSingle{#1}% + \crfnm@isFirstTokenfalse +} + +\def\crfnm@countInPrintedRefs{% + \ifcrfnm@simulated\else + % Since the incrementation is local, + % \crfnm@printedRefsNb will be automatically reset to 0 + % at the end of the current invocation of \crossrefenum. + \advance\crfnm@printedRefsNb by 1 + \fi +} + +\def\crfnm@wrapRangeOrSingle#1{% + \edef\crfnm@toBeWrapped{#1}% + \crfnm@ifIsRange{\crfnm@toBeWrapped}{% + \crfnm@wrapRange{\crfnm@toBeWrapped}% + }{% + \crfnm@ifIsDoubleRef{% + % We are in the primary part of a double type, + % since the secondary one is handled by \crfnm@fork + % like a simple type. + \crfnm@newCsnameAlias[\crfnm@typesetSingleRef]{crfnm@\crfnm@primarySubtype Ref}% + }{}% + \crfnm@typesetSingleRef{\crfnm@toBeWrapped}% + }% +} + +\def\crfnm@wrapRange#1{% + \expandafter\crfnm@wrap@range\expandafter[#1]% +} + +\expandafter\def\expandafter\crfnm@wrap@range\expandafter[\expandafter#\expandafter1\crfnm@labelRangeSep#2]{% + \crfnm@typesetRange{#1}{#2}% +} + +\def\crfnm@typesetRange#1#2{% + \edef\crfnm@refTypeForRange{% + \crfnm@ifIsDoubleRef{\crfnm@primarySubtype}{\crfnm@refType}% + }% + \edef\crfnm@beginRangeToTypeset{#1}% + \edef\crfnm@endRangeToTypeset{#2}% + \crfnm@typeset@range{\crfnm@beginRangeToTypeset}{\crfnm@endRangeToTypeset}[% + cs to get the raw reference number: crfnm@get\crfnm@refTypeForRange Number, + cs to print the reference: crfnm@\crfnm@refTypeForRange Ref% + ]% +} + +\def\crfnm@typeset@range#1#2[% + cs to get the raw reference number: #3, + cs to print the reference: #4% + ]{% + % #1 and #2 are the labels + \def\crfnm@getCountCsname{#3}% + \edef\crfnm@firstNumber{\csname\crfnm@getCountCsname\endcsname{#1}}% + \edef\crfnm@secondNumber{\csname\crfnm@getCountCsname\endcsname{#2}}% + \def\crfnm@typeset{\expandafter\csname #4\endcsname}% + \ifx\crfnm@firstNumber\crfnm@secondNumber + \crfnm@typeset{#1}% + \else + \crfnm@countInPrintedRefs + \crfnm@typeset{#1}\crfnm@rangeSep\crfnm@typeset{#2}% + \fi +} + +\def\crfnm@typesetDoubleRef#1#2{% + % #1 is a label + % #2 is a list of labels to be passed to \crossrefenum + \crfnm@ifIsInverted{% + \crfnm@typesetDoubleRef@inverted{#1}{#2}% + }{% + \crfnm@typesetDoubleRef@normal{#1}{#2}% + }% +} + +\def\crfnm@typesetDoubleRef@normal#1#2{% + \ifx\crfnm@groupSubtypes\crfnm@yes + \crfnm@typesetDoubleRef@normal@grouped{#1}{#2}% + \else + \crfnm@typesetDoubleRef@normal@split{#1}{#2}% + \fi +} + +\def\crfnm@typesetDoubleRef@normal@grouped#1#2{% + \crfnm@typesetPrefix + \crfnm@wrapInDisplayMacro{#1}% + \crfnm@separatorBetweenSubtypes + \crfnm@formatSecondary{% + \crfnm@fork{% + \crfnm@ifIsList[#2]{% + \edef\crfnm@enumOfSecondary{#2}% + }{% + \edef\crfnm@enumOfSecondary{{#2}}% + }% + \crfnm@enum[\crfnm@secondarySubtype][\crfnm@isSecondaryPrefixPrinted]{\crfnm@enumOfSecondary}% + }% + }% +} + +\def\crfnm@typesetDoubleRef@normal@split#1#2{% + \edef\crfnm@labelForPrimary{#1}% + \crfnm@ifIsRange\crfnm@labelForPrimary{% + \crfnm@typesetDoubleRef@normal@split@range{#1}{#2}% + }{% + \crfnm@typesetDoubleRef@normal@split@single{#1}{#2}% + }% +} + +\def\crfnm@typesetDoubleRef@normal@split@range#1#2{% + \edef\crfnm@labelForPrimary{#1}% + \edef\crfnm@beginRangeLabel{% + \crfnm@getLabelInRange@begin[\crfnm@labelForPrimary]% + }% + \edef\crfnm@endRangeLabel{% + \crfnm@getLabelInRange@end[\crfnm@labelForPrimary]% + }% + \edef\crfnm@beginPrimaryRaw{\crfnm@getRawValuePrimary\crfnm@beginRangeLabel}% + \edef\crfnm@endPrimaryRaw{\crfnm@getRawValuePrimary\crfnm@endRangeLabel}% + \ifx\crfnm@beginPrimaryRaw\crfnm@endPrimaryRaw + \edef\crfnm@labelsForSecondary{#2}% + \crfnm@typesetDoubleRef@normal@grouped{\crfnm@beginRangeLabel}{#2}% + \else + \crfnm@ifIsList[#2]{% + \edef\crfnm@allSecondaryLabels{#2}% + }{% + \edef\crfnm@allSecondaryLabels{{#2}}% + }% + \edef\crfnm@labelsForSecondary{% + \crfnm@replaceFirstInList[\crfnm@endRangeLabel]{\crfnm@allSecondaryLabels}% + }% + \crfnm@typesetPrefix + \crfnm@wrapInDisplayMacro{\crfnm@beginRangeLabel}% + \crfnm@separatorBetweenSubtypes + \crfnm@formatSecondary{% + \crfnm@fork{% + \crfnm@enum[\crfnm@secondarySubtype][\crfnm@isSecondaryPrefixPrinted]{% + {\crfnm@beginRangeLabel}% + }% + }% + }% + \crfnm@rangeSep + \crfnm@wrapInDisplayMacro{\crfnm@endRangeLabel}% + \crfnm@separatorBetweenSubtypes + \crfnm@formatSecondary{% + \crfnm@fork{% + \crfnm@enum[\crfnm@secondarySubtype][\crfnm@isSecondaryPrefixPrinted]{\crfnm@labelsForSecondary}% + }% + }% + \fi +} + +\def\crfnm@typesetDoubleRef@normal@split@single#1#2{% + \crfnm@typesetDoubleRef@normal@grouped{#1}{#2}% +} + +\def\crfnm@typesetDoubleRef@inverted#1#2{% + \crfnm@formatSecondary{% + \crfnm@fork{% + \crfnm@ifIsList[#2]{% + \edef\crfnm@enumOfSecondary{#2}% + }{% + \edef\crfnm@enumOfSecondary{{#2}}% + }% + \crfnm@enum[\crfnm@secondarySubtype][withprefix]{\crfnm@enumOfSecondary}% + }% + }% + \crfnm@separatorBetweenSubtypes + \crfnm@typesetPrefix + \crfnm@wrapInDisplayMacro{#1}% +} + +\def\crfnm@fork#1{% + \crfnm@saveState + \global\advance\crfnm@ienum by \crfnm@secondaryOfDouble@istart + \global\advance\crfnm@ienum by -1 + \crfnm@printedRefsNb=0 + #1% + \crfnm@restoreState +} + +\def\crfnm@saveState{% + \edef\crfnm@parentIenum{\the\crfnm@ienum}% + \edef\crfnm@parentPrintedRefsNb{\the\crfnm@printedRefsNb}% + \let\crfnm@parentCurrentPrimary\crfnm@currentPrimary + \let\crfnm@parentCurrentSecondary\crfnm@currentSecondary + \let\crfnm@parentPrecedingPrimary\crfnm@precedingPrimary + \let\crfnm@parentPrecedingSecondary\crfnm@precedingSecondary + \let\crfnm@parentRefType\crfnm@refType +} + +\def\crfnm@restoreState{% + \crfnm@isDoubleReftrue + \global\crfnm@ienum=\crfnm@parentIenum + \crfnm@printedRefsNb=\crfnm@parentPrintedRefsNb + \let\crfnm@currentPrimary\crfnm@parentCurrentPrimary + \let\crfnm@currentSecondary\crfnm@parentCurrentSecondary + \let\crfnm@precedingPrimary\crfnm@parentPrecedingPrimary + \let\crfnm@precedingSecondary\crfnm@parentPrecedingSecondary + \let\crfnm@refType\crfnm@parentRefType +} + +\catcode`\@=\crfnmOriginalCatcodeAt + diff --git a/Master/texmf-dist/tex/generic/crossrefenum/t-crossrefenum.tex b/Master/texmf-dist/tex/generic/crossrefenum/t-crossrefenum.tex new file mode 100644 index 00000000000..3f44e64f889 --- /dev/null +++ b/Master/texmf-dist/tex/generic/crossrefenum/t-crossrefenum.tex @@ -0,0 +1,18 @@ +%D \module +%D [ file=t-crossrefenum, +%D version=1.0, % May differ from crossrefenum.tex +%D title=\CONTEXT\ module for crossrefenum, +%D subtitle=Smart typesetting of enumerated cross-references for various TeX formats, +%D author=Bastien Dumont, +%D date=2022/11/11, +%D url=%TODO, +%D copyright=Bastien Dumont, +%D license=GNU Public License v. 3.0 or higher] + +\startmodule[crossrefenum] +\unprotect + +\input ../crossrefenum.tex + +\protect +\stopmodule
\ No newline at end of file |