% \iffalse meta-comment % % File: zref-clever.dtx % % This file is part of the LaTeX package "zref-clever". % % Copyright (C) 2021-2022 Gustavo Barros % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file: % % https://www.latex-project.org/lppl.txt % % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % % This work is "maintained" (as per LPPL maintenance status) by % Gustavo Barros. % % This work consists of the files zref-clever.dtx, % zref-clever.ins, % zref-clever.tex, % zref-clever-code.tex, % and the files listed in MANIFEST.md. % % The released version of this package is available from CTAN. % % ----------------------------------------------------------------------- % % The development version of the package can be found at % % https://github.com/gusbrs/zref-clever % % for those people who are interested. % % ----------------------------------------------------------------------- % % \fi % % \iffalse %<*driver> \documentclass{l3doc} % Have \GetFileInfo pick up date and version data and used in the % documentation. \usepackage[cap,nameinlink=false,check,titleref]{zref-clever} \begin{document} \DocInput{zref-clever.dtx} \end{document} % % \fi % % \DoNotIndex{\\,\{,\}} % \DoNotIndex{\c@,\cl@,\c@enumN,\p@} % \DoNotIndex{\the} % % \NewDocumentCommand\githubissue{m}{^^A % issue~\href{https://github.com/gusbrs/zref-clever/issues/#1}{\##1}} % % \NewDocumentCommand\githubPR{m}{^^A % PR~\href{https://github.com/gusbrs/zref-clever/pull/#1}{\##1}} % % ^^A Currently just for keeping semantic markup on this. % \NewDocumentCommand\contributor{m}{#1} % % \NewDocumentCommand\opt{m}{\texttt{#1}} % % \pdfstringdefDisableCommands{^^A % \def\opt#1{#1} % } % % ^^A Have the Index at 'section' level rather than 'part'. Otherwise it is % ^^A just the same definition from 'l3doc.cls'. % \IndexPrologue{^^A % \section*{Index} % \markboth{Index}{Index} % \addcontentsline{toc}{section}{Index} % The italic numbers denote the pages where the corresponding entry is % described, numbers underlined point to the definition, all others indicate % the places where it is used.^^A % } % % % \GetFileInfo{zref-clever.sty} % % \title{^^A % The \pkg{zref-clever} package^^A % \thanks{This file describes \fileversion, released \filedate.}^^A % \texorpdfstring{\\{}\medskip{}}{ - }^^A % Code documentation^^A % \texorpdfstring{\medskip{}}{}^^A % } % % \author{^^A % Gustavo Barros^^A % \thanks{\url{https://github.com/gusbrs/zref-clever}}^^A % } % % \date{\filedate} % % \maketitle % % \begin{center} % \textbf{EXPERIMENTAL} % \end{center} % % % \tableofcontents % % % \section{Initial setup} % % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*package> % \end{macrocode} % % Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention). % \begin{macrocode} %<@@=zrefclever> % \end{macrocode} % % Taking a stance on backward compatibility of the package. During initial % development, we have used freely recent features of the kernel (albeit % refraining from \pkg{l3candidates}, even though I'd have loved to have used % \cs{bool_case_true:}\dots{}). We presume \pkg{xparse} (which made to the % kernel in the 2020-10-01 release), and \pkg{expl3} as well (which made to % the kernel in the 2020-02-02 release). We also just use UTF-8 for the % language files (which became the default input encoding in the 2018-04-01 % release). Finally, a couple of changes came with the 2021-11-15 kernel % release, which are important here. First, a fix was made to the new hook % management system (\pkg{ltcmdhooks}), with implications to the hook we add % to \cs{appendix} (by \contributor{Phelype Oleinik} at % \url{https://tex.stackexchange.com/q/617905} and % \url{https://github.com/latex3/latex2e/pull/699}). Second, the support for % \cs{@currentcounter} has been improved, including \cs{footnote} and % \pkg{amsmath} (by \contributor{Frank Mittelbach} and \contributor{Ulrike % Fischer} at \url{https://github.com/latex3/latex2e/issues/687}). Hence, % since we would not be able to go much backwards without special handling % anyway, we make the cut at the 2021-11-15 kernel release. % % \begin{macrocode} \providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion} \IfFormatAtLeastTF{2021-11-15} {} {% \PackageError{zref-clever}{LaTeX kernel too old} {% 'zref-clever' requires a LaTeX kernel 2021-11-15 or newer.% \MessageBreak Loading will abort!% }% \endinput }% % \end{macrocode} % % % Identify the package. % \begin{macrocode} \ProvidesExplPackage {zref-clever} {2022-01-10} {0.1.2-alpha} {Clever LaTeX cross-references based on zref} % \end{macrocode} % % % \section{Dependencies} % % Required packages. Besides these, \pkg{zref-hyperref}, \pkg{zref-titleref}, % and \pkg{zref-check} may also be loaded depending on user options. % % \begin{macrocode} \RequirePackage { zref-base } \RequirePackage { zref-user } \RequirePackage { zref-abspage } \RequirePackage { l3keys2e } \RequirePackage { ifdraft } % \end{macrocode} % % % \section{\pkg{zref} setup} % % For the purposes of the package, we need to store some information with the % labels, some of it standard, some of it not so much. So, we have to setup % \pkg{zref} to do so. % % Some basic properties are handled by \pkg{zref} itself, or some of its % modules. The \texttt{default} and \texttt{page} properties are provided by % \pkg{zref-base}, while \pkg{zref-abspage} provides the \texttt{abspage} % property which gives us a safe and easy way to sort labels for page % references. % % The \texttt{counter} property, in most cases, will be just the kernel's % \cs{@currentcounter}, set by \cs{refstepcounter}. However, not everywhere % is it assured that \cs{@currentcounter} gets updated as it should, so we % need to have some means to manually tell \pkg{zref-clever} what the current % counter actually is. This is done with the \opt{currentcounter} option, and % stored in \cs{l_@@_current_counter_tl}, whose default is % \cs{@currentcounter}. % % \begin{macrocode} \zref@newprop { zc@counter } { \l_@@_current_counter_tl } \zref@addprop \ZREF@mainlist { zc@counter } % \end{macrocode} % % The reference itself, stored by \pkg{zref-base} in the \texttt{default} % property, is somewhat a disputed real estate. In particular, the use of % \cs{labelformat} (previously from \pkg{varioref}, now in the kernel) will % include there the reference ``prefix'' and complicate the job we are trying % to do here. Hence, we isolate \cs[no-index]{the}\meta{counter} and store it % ``clean'' in \texttt{thecounter} for reserved use. Since % \cs{@currentlabel}, which populates the \texttt{default} property, is % \emph{more reliable} than \cs{@currentcounter}, \texttt{thecounter} is meant % to be kept as an \emph{option} (\opt{ref} option), in case there's need to % use \pkg{zref-clever} together with \cs{labelformat}. Based on the % definition of \cs{@currentlabel} done inside \cs{refstepcounter} in % \texttt{texdoc source2e}, section \texttt{ltxref.dtx}. We just drop the % \cs[no-index]{p@...} prefix. % % \begin{macrocode} \zref@newprop { thecounter } { \cs_if_exist:cTF { c@ \l_@@_current_counter_tl } { \use:c { the \l_@@_current_counter_tl } } { \cs_if_exist:cT { c@ \@currentcounter } { \use:c { the \@currentcounter } } } } \zref@addprop \ZREF@mainlist { thecounter } % \end{macrocode} % % % Much of the work of \pkg{zref-clever} relies on the association between a % label's ``counter'' and its ``type'' (see the User manual section on % ``Reference types''). Superficially examined, one might think this relation % could just be stored in a global property list, rather than in the label % itself. However, there are cases in which we want to distinguish different % types for the same counter, depending on the document context. Hence, we % need to store the ``type'' of the ``counter'' for each ``label''. In % setting this, the presumption is that the label's type has the same name as % its counter, unless it is specified otherwise by the \opt{countertype} % option, as stored in \cs{l_@@_counter_type_prop}. % % \begin{macrocode} \zref@newprop { zc@type } { \exp_args:NNe \prop_if_in:NnTF \l_@@_counter_type_prop \l_@@_current_counter_tl { \exp_args:NNe \prop_item:Nn \l_@@_counter_type_prop { \l_@@_current_counter_tl } } { \l_@@_current_counter_tl } } \zref@addprop \ZREF@mainlist { zc@type } % \end{macrocode} % % % Since the \texttt{default}/\texttt{thecounter} and \texttt{page} properties % store the ``\emph{printed} representation'' of their respective counters, % for sorting and compressing purposes, we are also interested in their % numeric values. So we store them in \texttt{zc@cntval} and % \texttt{zc@pgval}. For this, we use \cs[no-index]{c@}\meta{counter}, which % contains the counter's numerical value (see `texdoc source2e', section % `ltcounts.dtx'). % \begin{macrocode} \zref@newprop { zc@cntval } [0] { \cs_if_exist:cTF { c@ \l_@@_current_counter_tl } { \int_use:c { c@ \l_@@_current_counter_tl } } { \cs_if_exist:cT { c@ \@currentcounter } { \int_use:c { c@ \@currentcounter } } } } \zref@addprop \ZREF@mainlist { zc@cntval } \zref@newprop* { zc@pgval } [0] { \int_use:c { c@page } } \zref@addprop \ZREF@mainlist { zc@pgval } % \end{macrocode} % % % However, since many counters (may) get reset along the document, we require % more than just their numeric values. We need to know the reset chain of a % given counter, in order to sort and compress a group of references. Also % here, the ``printed representation'' is not enough, not only because it is % easier to work with the numeric values but, given we occasionally group % multiple counters within a single type, sorting this group requires to know % the actual counter reset chain. % % Furthermore, even if it is true that most of the definitions of counters, % and hence of their reset behavior, is likely to be defined in the preamble, % this is not necessarily true. Users can create counters, newtheorems % mid-document, and alter their reset behavior along the way. Was that not % the case, we could just store the desired information at % \texttt{begindocument} in a variable and retrieve it when needed. But since % it is, we need to store the information with the label, with the values as % current when the label is set. % % Though counters can be reset at any time, and in different ways at that, the % most important use case is the automatic resetting of counters when some % other counter is stepped, as performed by the standard mechanisms of the % kernel (optional argument of \cs{newcounter}, \cs{@addtoreset}, % \cs{counterwithin}, and related infrastructure). The canonical optional % argument of \cs{newcounter} establishes that the counter being created (the % mandatory argument) gets reset every time the ``enclosing counter'' gets % stepped (this is called in the usual sources ``within-counter'', ``old % counter'', ``supercounter'', ``parent counter'' etc.). This information is % somewhat tricky to get. For starters, the counters which may reset the % current counter are not retrievable from the counter itself, because this % information is stored with the counter that does the resetting, not with the % one that gets reset (the list is stored in \cs[no-index]{cl@}\meta{counter} % with format % \texttt{\cs{@elt}\{countera\}\cs{@elt}\{counterb\}\cs{@elt}\{counterc\}}, % see \file{ltcounts.dtx} in \texttt{texdoc source2e}). Besides, there may be % a chain of resetting counters, which must be taken into account: if % \texttt{counterC} gets reset by \texttt{counterB}, and \texttt{counterB} % gets reset by \texttt{counterA}, stepping the latter affects all three of % them. % % The procedure below examines a set of counters, those in % \cs{l_@@_counter_resetters_seq}, and for each of them retrieves the set of % counters it resets, as stored in \cs[no-index]{cl@}\meta{counter}, looking % for the counter for which we are trying to set a label % (\cs{l_@@_current_counter_tl}, by default \cs{@currentcounter}, passed as an % argument to the functions). There is one relevant caveat to this procedure: % \cs{l_@@_counter_resetters_seq} is populated by hand with the ``usual % suspects'', there is no way (that I know of) to ensure it is exhaustive. % However, it is not that difficult to create a reasonable ``usual suspects'' % list which, of course, should include the counters for the sectioning % commands to start with, and it is easy to add more counters to this list if % needed, with the option \opt{counterresetters}. Unfortunately, not all % counters are created alike, or reset alike. Some counters, even some kernel % ones, get reset by other mechanisms (notably, the \texttt{enumerate} % environment counters do not use the regular counter machinery for resetting % on each level, but are nested nevertheless by other means). Therefore, % inspecting \cs[no-index]{cl@}\meta{counter} cannot possibly fully account % for all of the automatic counter resetting which takes place in the % document. And there's also no other ``general rule'' we could grab on for % this, as far as I know. So we provide a way to manually tell % \pkg{zref-clever} of these cases, by means of the \opt{counterresetby} % option, whose information is stored in \cs{l_@@_counter_resetby_prop}. This % manual specification has precedence over the search through % \cs{l_@@_counter_resetters_seq}, and should be handled with care, since % there is no possible verification mechanism for this. % % % \begin{macro}[EXP]{\@@_get_enclosing_counters_value:n} % Recursively generate a \emph{sequence} of ``enclosing counters'' values, % for a given \meta{counter} and leave it in the input stream. This % function must be expandable, since it gets called from \cs{zref@newprop} % and is the one responsible for generating the desired information when % the label is being set. Note that the order in which we are getting this % information is reversed, since we are navigating the counter reset chain % bottom-up. But it is very hard to do otherwise here where we need % expandable functions, and easy to handle at the reading side. % \begin{syntax} % \cs{@@_get_enclosing_counters_value:n} \Arg{counter} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_get_enclosing_counters_value:n #1 { \cs_if_exist:cT { c@ \@@_counter_reset_by:n {#1} } { { \int_use:c { c@ \@@_counter_reset_by:n {#1} } } \@@_get_enclosing_counters_value:e { \@@_counter_reset_by:n {#1} } } } % \end{macrocode} % % Both \texttt{e} and \texttt{f} expansions work for this particular recursive % call. I'll stay with the \texttt{e} variant, since conceptually it is what % I want (\texttt{x} itself is not expandable), and this package is anyway not % compatible with older kernels for which the performance penalty of the % \texttt{e} expansion would ensue (helpful comment by \contributor{Enrico % Gregorio}, aka `egreg' at % \url{https://tex.stackexchange.com/q/611370/#comment1529282_611385}). % \begin{macrocode} \cs_generate_variant:Nn \@@_get_enclosing_counters_value:n { e } % \end{macrocode} % \end{macro} % % % \begin{macro}[EXP]{\@@_counter_reset_by:n} % Auxiliary function for \cs{@@_get_enclosing_counters_value:n}, and useful % on its own standing. It is broken in parts to be able to use the % expandable mapping functions. \cs{@@_counter_reset_by:n} leaves in the % stream the ``enclosing counter'' which resets \meta{counter}. % \begin{syntax} % \cs{@@_counter_reset_by:n} \Arg{counter} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_counter_reset_by:n #1 { \bool_if:nTF { \prop_if_in_p:Nn \l_@@_counter_resetby_prop {#1} } { \prop_item:Nn \l_@@_counter_resetby_prop {#1} } { \seq_map_tokens:Nn \l_@@_counter_resetters_seq { \@@_counter_reset_by_aux:nn {#1} } } } \cs_new:Npn \@@_counter_reset_by_aux:nn #1#2 { \cs_if_exist:cT { c@ #2 } { \tl_if_empty:cF { cl@ #2 } { \tl_map_tokens:cn { cl@ #2 } { \@@_counter_reset_by_auxi:nnn {#2} {#1} } } } } \cs_new:Npn \@@_counter_reset_by_auxi:nnn #1#2#3 { \str_if_eq:nnT {#2} {#3} { \tl_map_break:n { \seq_map_break:n {#1} } } } % \end{macrocode} % \end{macro} % % % Finally, we create the \texttt{zc@enclval} property, and add it to the % \texttt{main} property list. % \begin{macrocode} \zref@newprop { zc@enclval } { \@@_get_enclosing_counters_value:e \l_@@_current_counter_tl } \zref@addprop \ZREF@mainlist { zc@enclval } % \end{macrocode} % % % Another piece of information we need is the page numbering format being used % by \cs{thepage}, so that we know when we can (or not) group a set of page % references in a range. Unfortunately, \texttt{page} is not a typical % counter in ways which complicates things. First, it does commonly get reset % along the document, not necessarily by the usual counter reset chains, but % rather with \cs{pagenumbering} or variations thereof. Second, the format of % the page number commonly changes in the document (roman, arabic, etc.), not % necessarily, though usually, together with a reset. Trying to ``parse'' % \cs{thepage} to retrieve such information is bound to go wrong: we don't % know, and can't know, what is within that macro, and that's the business of % the user, or of the documentclass, or of the loaded packages. The technique % used by \pkg{cleveref}, which we borrow here, is simple and smart: store % with the label what \cs{thepage} would return, if the counter \cs{c@page} % was ``\(1\)''. That does not allow us to \emph{sort} the references, % luckily however, we have \texttt{abspage} which solves this problem. But we % can decide whether two labels can be compressed into a range or not based on % this format: if they are identical, we can compress them, otherwise, we % can't. To do so, we locally redefine \cs{c@page} to return ``1'', thus % avoiding any global spillovers of this trick. Since this operation is not % expandable we cannot run it directly from the property definition. Hence, % we use a shipout hook, and set \cs{g_@@_page_format_tl}, which can then be % retrieved by the starred definition of % \texttt{\cs{zref@newprop}*\{zc@pgfmt\}}. % % \begin{macrocode} \tl_new:N \g_@@_page_format_tl \cs_new_protected:Npx \@@_page_format_aux: { \int_eval:n { 1 } } \AddToHook { shipout / before } { \group_begin: \cs_set_eq:NN \c@page \@@_page_format_aux: \tl_gset:Nx \g_@@_page_format_tl { \thepage } \group_end: } \zref@newprop* { zc@pgfmt } { \g_@@_page_format_tl } \zref@addprop \ZREF@mainlist { zc@pgfmt } % \end{macrocode} % % % Still some other properties which we don't need to handle at the data % provision side, but need to cater for at the retrieval side, are the ones % from the \pkg{zref-xr} module, which are added to the labels imported from % external documents, and needed to construct hyperlinks to them and to % distinguish them from the current document ones at sorting and compressing: % \texttt{urluse}, \texttt{url} and \texttt{externaldocument}. % % % % \section{Plumbing} % % % \subsection{Auxiliary} % % % \begin{macro} % { % \@@_if_package_loaded:n , % \@@_if_class_loaded:n , % } % Just a convenience, since sometimes we just need one of the branches, and % it is particularly easy to miss the empty F branch after a long T one. % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_package_loaded:n #1 { T , F , TF } { \IfPackageLoadedTF {#1} { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Npnn \@@_if_class_loaded:n #1 { T , F , TF } { \IfClassLoadedTF {#1} { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % % \subsection{Messages} % % % \begin{macrocode} \msg_new:nnn { zref-clever } { option-not-type-specific } { Option~'#1'~is~not~type-specific~\msg_line_context:.~ Set~it~in~'\iow_char:N\\zcLanguageSetup'~before~first~'type'~ switch~or~as~package~option. } \msg_new:nnn { zref-clever } { option-only-type-specific } { No~type~specified~for~option~'#1'~\msg_line_context:.~ Set~it~after~'type'~switch. } \msg_new:nnn { zref-clever } { key-requires-value } { The~'#1'~key~'#2'~requires~a~value~\msg_line_context:. } \msg_new:nnn { zref-clever } { key-boolean-or-empty } { The~key~'#1'~only~accepts~the~values~'true',~'false'~ or~an~empty~value~\msg_line_context:. } \msg_new:nnn { zref-clever } { language-declared } { Language~'#1'~is~already~declared~\msg_line_context:.~Nothing~to~do. } \msg_new:nnn { zref-clever } { unknown-language-alias } { Language~'#1'~is~unknown~\msg_line_context:.~Can't~alias~to~it.~ See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~ '\iow_char:N\\zcDeclareLanguageAlias'. } \msg_new:nnn { zref-clever } { unknown-language-setup } { Language~'#1'~is~unknown~\msg_line_context:.~Can't~set~it~up.~ See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~ '\iow_char:N\\zcDeclareLanguageAlias'. } \msg_new:nnn { zref-clever } { unknown-language-opt } { Language~'#1'~is~unknown~\msg_line_context:.~Using~default.~ See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~ '\iow_char:N\\zcDeclareLanguageAlias'. } \msg_new:nnn { zref-clever } { unknown-language-decl } { Can't~set~declension~'#1'~for~unknown~language~'#2'~\msg_line_context:.~ See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~ '\iow_char:N\\zcDeclareLanguageAlias'. } \msg_new:nnn { zref-clever } { language-no-decl-ref } { Language~'#1'~has~no~declared~declension~cases~\msg_line_context:.~ Nothing~to~do~with~option~'d=#2'. } \msg_new:nnn { zref-clever } { language-no-gender } { Language~'#1'~has~no~declared~gender~\msg_line_context:.~ Nothing~to~do~with~option~'#2=#3'. } \msg_new:nnn { zref-clever } { language-no-decl-setup } { Language~'#1'~has~no~declared~declension~cases~\msg_line_context:.~ Nothing~to~do~with~option~'case=#2'. } \msg_new:nnn { zref-clever } { unknown-decl-case } { Declension~case~'#1'~unknown~for~language~'#2'~\msg_line_context:.~ Using~default~declension~case. } \msg_new:nnn { zref-clever } { nudge-multitype } { Reference~with~multiple~types~\msg_line_context:.~ You~may~wish~to~separate~them~or~review~language~around~it. } \msg_new:nnn { zref-clever } { nudge-comptosing } { Multiple~labels~have~been~compressed~into~singular~type~name~ for~type~'#1'~\msg_line_context:. } \msg_new:nnn { zref-clever } { nudge-plural-when-sg } { Option~'sg'~signals~that~a~singular~type~name~was~expected~ \msg_line_context:.~But~type~'#1'~has~plural~type~name. } \msg_new:nnn { zref-clever } { gender-not-declared } { Language~'#1'~has~no~'#2'~gender~declared~\msg_line_context:. } \msg_new:nnn { zref-clever } { nudge-gender-mismatch } { Gender~mismatch~for~type~'#1'~\msg_line_context:.~ You've~specified~'g=#2'~but~type~name~is~'#3'~for~language~'#4'. } \msg_new:nnn { zref-clever } { nudge-gender-not-declared-for-type } { You've~specified~'g=#1'~\msg_line_context:.~ But~gender~for~type~'#2'~is~not~declared~for~language~'#3'. } \msg_new:nnn { zref-clever } { nudgeif-unknown-value } { Unknown~value~'#1'~for~'nudgeif'~option~\msg_line_context:. } \msg_new:nnn { zref-clever } { option-document-only } { Option~'#1'~is~only~available~after~\iow_char:N\\begin\{document\}. } \msg_new:nnn { zref-clever } { langfile-loaded } { Loaded~'#1'~language~file. } \msg_new:nnn { zref-clever } { langfile-not-available } { Language~file~for~'#1'~not~available~\msg_line_context:. } \msg_new:nnn { zref-clever } { unknown-language-load } { Language~'#1'~is~unknown~\msg_line_context:.~ Unable~to~load~language~file.~See~documentation~for~ '\iow_char:N\\zcDeclareLanguage'~and~ '\iow_char:N\\zcDeclareLanguageAlias'. } \msg_new:nnn { zref-clever } { zref-property-undefined } { Option~'ref=#1'~requested~\msg_line_context:.~ But~the~property~'#1'~is~not~declared,~falling-back~to~'default'. } \msg_new:nnn { zref-clever } { hyperref-preamble-only } { Option~'hyperref'~only~available~in~the~preamble~\msg_line_context:.~ To~inhibit~hyperlinking~locally,~you~can~use~the~starred~version~of~ '\iow_char:N\\zcref'. } \msg_new:nnn { zref-clever } { missing-hyperref } { Missing~'hyperref'~package.~Setting~'hyperref=false'. } \msg_new:nnn { zref-clever } { titleref-preamble-only } { Option~'titleref'~only~available~in~the~preamble~\msg_line_context:.~ Did~you~mean~'ref=title'?. } \msg_new:nnn { zref-clever } { option-preamble-only } { Option~'#1'~only~available~in~the~preamble~\msg_line_context:. } \msg_new:nnn { zref-clever } { unknown-compat-module } { Unknown~compatibility~module~'#1'~given~to~option~'nocompat'.~ Nothing~to~do. } \msg_new:nnn { zref-clever } { missing-zref-check } { Option~'check'~requested~\msg_line_context:.~ But~package~'zref-check'~is~not~loaded,~can't~run~the~checks. } \msg_new:nnn { zref-clever } { missing-type } { Reference~type~undefined~for~label~'#1'~\msg_line_context:. } \msg_new:nnn { zref-clever } { missing-property } { Reference~property~'#1'~undefined~for~label~'#2'~\msg_line_context:. } \msg_new:nnn { zref-clever } { missing-name } { Reference~format~option~'#1'~undefined~for~type~'#2'~\msg_line_context:. } \msg_new:nnn { zref-clever } { missing-string } { We~couldn't~find~a~value~for~reference~option~'#1'~\msg_line_context:.~ But~we~should~have:~throw~a~rock~at~the~maintainer. } \msg_new:nnn { zref-clever } { single-element-range } { Range~for~type~'#1'~resulted~in~single~element~\msg_line_context:. } \msg_new:nnn { zref-clever } { compat-package } { Loaded~support~for~'#1'~package. } \msg_new:nnn { zref-clever } { compat-class } { Loaded~support~for~'#1'~documentclass. } \msg_new:nnn { zref-clever } { option-deprecated } { Option~'#1'~has~been~deprecated~\msg_line_context:.\iow_newline: Use~'#2'~instead. } % \end{macrocode} % % % \subsection{Data extraction} % % % \begin{macro}{\@@_extract_default:Nnnn} % Extract property \meta{prop} from \meta{label} and sets variable \meta{tl % var} with extracted value. Ensure \cs{zref@extractdefault} is expanded % exactly twice, but no further to retrieve the proper value. In case the % property is not found, set \meta{tl var} with \meta{default}. % \begin{syntax} % \cs{@@_extract_default:Nnnn} \Arg{tl val} % ~~\Arg{label} \Arg{prop} \Arg{default} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_extract_default:Nnnn #1#2#3#4 { \exp_args:NNNo \exp_args:NNo \tl_set:Nn #1 { \zref@extractdefault {#2} {#3} {#4} } } \cs_generate_variant:Nn \@@_extract_default:Nnnn { NVnn } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_extract_unexp:nnn} % Extract property \meta{prop} from \meta{label}. Ensure that, in the % context of an x expansion, \cs{zref@extractdefault} is expanded exactly % twice, but no further to retrieve the proper value. Thus, this is meant % to be use in an x expansion context, not in other situations. In case the % property is not found, leave \meta{default} in the stream. % \begin{syntax} % \cs{@@_extract_unexp:nnn}\Arg{label}\Arg{prop}\Arg{default} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_extract_unexp:nnn #1#2#3 { \exp_args:NNo \exp_args:No \exp_not:n { \zref@extractdefault {#1} {#2} {#3} } } \cs_generate_variant:Nn \@@_extract_unexp:nnn { Vnn , nvn , Vvn } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_extract:nnn} % An internal version for \cs{zref@extractdefault}. % \begin{syntax} % \cs{@@_extract:nnn}\Arg{label}\Arg{prop}\Arg{default} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_extract:nnn #1#2#3 { \zref@extractdefault {#1} {#2} {#3} } % \end{macrocode} % \end{macro} % % % \subsection{Reference format} % % For a general discussion on the precedence rules for reference format % options, see Section ``Reference format'' in the User manual. Internally, % these precedence rules are handled / enforced in % \cs{@@_get_ref_opt_typeset:nN}, \cs{@@_get_ref_opt_font:nN}, and % \cs{@@_type_name_setup:} which are the basic functions to retrieve proper % values for reference format settings. The ``fallback'' settings are stored % in \cs{g_@@_fallback_unknown_lang_prop}. % % % \begin{macro} % { % \l_@@_setup_type_tl , % \l_@@_base_language_tl , % \l_@@_lang_decl_case_tl , % \l_@@_lang_declension_seq , % \l_@@_lang_gender_seq , % } % Store ``current'' type, language, and declension cases in different places % for type-specific and language-specific options handling, notably in % \cs{@@_provide_langfile:n}, \cs{zcRefTypeSetup}, and % \cs{zcLanguageSetup}. But also for language options retrieval, in % \cs{@@_get_lang_opt_type:nnnN} and \cs{@@_get_lang_opt_default:nnN}. % \begin{macrocode} \tl_new:N \l_@@_setup_type_tl \tl_new:N \l_@@_base_language_tl \tl_new:N \l_@@_lang_decl_case_tl \seq_new:N \l_@@_lang_declension_seq \seq_new:N \l_@@_lang_gender_seq % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \c_@@_ref_options_necessarily_not_type_specific_seq , % \c_@@_ref_options_possibly_type_specific_seq , % \c_@@_ref_options_type_names_seq , % \c_@@_ref_options_font_seq , % \c_@@_ref_options_typesetup_seq , % \c_@@_ref_options_reference_seq , % } % Lists of reference format related options in ``categories''. Since % these options are set in different scopes, and at different places, % storing the actual lists in centralized variables makes the job not only % easier later on, but also keeps things consistent. % \begin{macrocode} \seq_const_from_clist:Nn \c_@@_ref_options_necessarily_not_type_specific_seq { tpairsep , tlistsep , tlastsep , notesep , } \seq_const_from_clist:Nn \c_@@_ref_options_possibly_type_specific_seq { namesep , pairsep , listsep , lastsep , rangesep , preref , postref , } % \end{macrocode} % Only ``type names'' are ``necessarily type-specific'', which makes them % somewhat special on the retrieval side of things. In short, they don't have % their values queried by \cs{@@_get_ref_opt_typeset:nN}, but by % \cs{@@_type_name_setup:}. % \begin{macrocode} \seq_const_from_clist:Nn \c_@@_ref_options_type_names_seq { Name-sg , name-sg , Name-pl , name-pl , Name-sg-ab , name-sg-ab , Name-pl-ab , name-pl-ab , } % \end{macrocode} % \cs{c_@@_ref_options_font_seq} are technically ``possibly type-specific'', % but are not ``language-specific'', so we separate them. % \begin{macrocode} \seq_const_from_clist:Nn \c_@@_ref_options_font_seq { namefont , reffont , } % \end{macrocode} % And, finally, some combined groups of the above variables, for convenience. % \begin{macrocode} \seq_new:N \c_@@_ref_options_typesetup_seq \seq_gconcat:NNN \c_@@_ref_options_typesetup_seq \c_@@_ref_options_possibly_type_specific_seq \c_@@_ref_options_type_names_seq \seq_gconcat:NNN \c_@@_ref_options_typesetup_seq \c_@@_ref_options_typesetup_seq \c_@@_ref_options_font_seq \seq_new:N \c_@@_ref_options_reference_seq \seq_gconcat:NNN \c_@@_ref_options_reference_seq \c_@@_ref_options_necessarily_not_type_specific_seq \c_@@_ref_options_possibly_type_specific_seq \seq_gconcat:NNN \c_@@_ref_options_reference_seq \c_@@_ref_options_reference_seq \c_@@_ref_options_font_seq % \end{macrocode} % \end{macro} % % % % \subsection{Languages} % % \begin{macro}{\g_@@_languages_prop} % Stores the names of known languages and the mapping from ``language name'' % to ``base language name''. Whether or not a language or alias is known to % \pkg{zref-clever} is decided by its presence in this property list. A % ``base language'' (loose concept here, meaning just ``the name we gave for % the language file in that particular language'') is just like any other % one, the only difference is that the ``language name'' happens to be the % same as the ``base language name'', in other words, it is an ``alias to % itself''. % \begin{macrocode} \prop_new:N \g_@@_languages_prop % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\zcDeclareLanguage} % Declare a new language for use with \pkg{zref-clever}. \meta{language} is % taken to be both the ``language name'' and the ``base language name''. % \oarg{options} receive a \texttt{k=v} set of options, with three valid % options. The first, \opt{declension}, takes the noun declension cases % prefixes for \meta{language} as a comma separated list, whose first % element is taken to be the default case. The second, \opt{gender}, % receives the genders for \meta{language} as comma separated list. The % third, \opt{allcaps}, receives no value, and indicates that for % \meta{language} all nouns must be capitalized for grammatical reasons, in % which case, the \opt{cap} option is disregarded for \meta{language}. If % \meta{language} is already known, just warn. This implies a particular % restriction regarding \oarg{options}, namely that these options, when % defined by the package, cannot be redefined by the user. This is % deliberate, otherwise the built-in language files would become much too % sensitive to this particular user input, and unnecessarily so. % \cs{zcDeclareLanguage} is preamble only. % \begin{syntax} % \cs{zcDeclareLanguage} \oarg{options} \marg{language} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcDeclareLanguage { O { } m } { \group_begin: \tl_if_empty:nF {#2} { \prop_if_in:NnTF \g_@@_languages_prop {#2} { \msg_warning:nnn { zref-clever } { language-declared } {#2} } { \prop_gput:Nnn \g_@@_languages_prop {#2} {#2} \prop_new:c { g_@@_lang_ #2 _prop } \tl_set:Nn \l_@@_base_language_tl {#2} \keys_set:nn { zref-clever / declarelang } {#1} } } \group_end: } \@onlypreamble \zcDeclareLanguage % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\zcDeclareLanguageAlias} % Declare \meta{language alias} to be an alias of \meta{aliased language} % (or ``base language''). \meta{aliased language} must be already known to % \pkg{zref-clever}, as stored in \cs{g_@@_languages_prop}. % \cs{zcDeclareLanguageAlias} is preamble only. % \begin{syntax} % \cs{zcDeclareLanguageAlias} \marg{language alias} \marg{aliased language} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcDeclareLanguageAlias { m m } { \tl_if_empty:nF {#1} { \prop_if_in:NnTF \g_@@_languages_prop {#2} { \exp_args:NNnx \prop_gput:Nnn \g_@@_languages_prop {#1} { \prop_item:Nn \g_@@_languages_prop {#2} } } { \msg_warning:nnn { zref-clever } { unknown-language-alias } {#2} } } } \@onlypreamble \zcDeclareLanguageAlias % \end{macrocode} % \end{macro} % % % % \begin{macrocode} \keys_define:nn { zref-clever / declarelang } { declension .code:n = { \prop_gput:cnn { g_@@_lang_ \l_@@_base_language_tl _prop } { declension } {#1} } , declension .value_required:n = true , gender .code:n = { \prop_gput:cnn { g_@@_lang_ \l_@@_base_language_tl _prop } { gender } {#1} } , gender .value_required:n = true , allcaps .code:n = { \prop_gput:cnn { g_@@_lang_ \l_@@_base_language_tl _prop } { allcaps } { true } } , allcaps .value_forbidden:n = true , } % \end{macrocode} % % % % \begin{macro}{\@@_process_language_options:} % Auxiliary function for \cs{@@_zcref:nnn}, responsible for processing % options from \cs{zcDeclareLanguage}. It is necessary to separate them % from the reference options machinery because their behavior is language % dependent, but the language itself can also be set as an option % (\opt{lang}, value stored in \cs{l_@@_ref_language_tl}). Hence, we must % validate these options after the reference options have been set. It is % expected to be called right (or soon) after \cs{keys_set:nn} in % \cs{@@_zcref:nnn}, where current values for \cs{l_@@_ref_language_tl} and % \cs{l_@@_ref_decl_case_tl} are in place. % \begin{macrocode} \cs_new_protected:Npn \@@_process_language_options: { \exp_args:NNx \prop_get:NnNTF \g_@@_languages_prop { \l_@@_ref_language_tl } \l_@@_base_language_tl { % \end{macrocode} % Validate the declension case (\opt{d}) option against the declared cases for % the reference language. If the user value for the latter does not match the % declension cases declared for the former, the function sets an appropriate % value for \cs{l_@@_ref_decl_case_tl}, either using the default case, or % clearing the variable, depending on the language setup. And also issues a % warning about it. % \begin{macrocode} \exp_args:NNx \seq_set_from_clist:Nn \l_@@_lang_declension_seq { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { declension } } \seq_if_empty:NTF \l_@@_lang_declension_seq { \tl_if_empty:NF \l_@@_ref_decl_case_tl { \msg_warning:nnxx { zref-clever } { language-no-decl-ref } { \l_@@_ref_language_tl } { \l_@@_ref_decl_case_tl } \tl_clear:N \l_@@_ref_decl_case_tl } } { \tl_if_empty:NTF \l_@@_ref_decl_case_tl { \seq_get_left:NN \l_@@_lang_declension_seq \l_@@_ref_decl_case_tl } { \seq_if_in:NVF \l_@@_lang_declension_seq \l_@@_ref_decl_case_tl { \msg_warning:nnxx { zref-clever } { unknown-decl-case } { \l_@@_ref_decl_case_tl } { \l_@@_ref_language_tl } \seq_get_left:NN \l_@@_lang_declension_seq \l_@@_ref_decl_case_tl } } } % \end{macrocode} % Validate the gender (\opt{g}) option against the declared genders for the % reference language. If the user value for the latter does not match the % genders declared for the former, clear \cs{l_@@_ref_gender_tl} and warn. % \begin{macrocode} \exp_args:NNx \seq_set_from_clist:Nn \l_@@_lang_gender_seq { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { gender } } \seq_if_empty:NTF \l_@@_lang_gender_seq { \tl_if_empty:NF \l_@@_ref_gender_tl { \msg_warning:nnxxx { zref-clever } { language-no-gender } { \l_@@_ref_language_tl } { g } { \l_@@_ref_gender_tl } \tl_clear:N \l_@@_ref_gender_tl } } { \tl_if_empty:NF \l_@@_ref_gender_tl { \seq_if_in:NVF \l_@@_lang_gender_seq \l_@@_ref_gender_tl { \msg_warning:nnxx { zref-clever } { gender-not-declared } { \l_@@_ref_language_tl } { \l_@@_ref_gender_tl } \tl_clear:N \l_@@_ref_gender_tl } } } % \end{macrocode} % Ensure the \opt{cap} in \cs{l_@@_ref_options_prop} is set to \texttt{true} % when the language was declared with \opt{allcaps} option. % \begin{macrocode} \str_if_eq:eeT { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { allcaps } } { true } { \prop_put:Nnn \l_@@_ref_options_prop { cap } { true } } } { % \end{macrocode} % If the language itself is not declared, we still have to issue declension % and gender warnings, if \opt{d} or \opt{g} options were used. % \begin{macrocode} \tl_if_empty:NF \l_@@_ref_decl_case_tl { \msg_warning:nnxx { zref-clever } { unknown-language-decl } { \l_@@_ref_decl_case_tl } { \l_@@_ref_language_tl } \tl_clear:N \l_@@_ref_decl_case_tl } \tl_if_empty:NF \l_@@_ref_gender_tl { \msg_warning:nnxxx { zref-clever } { language-no-gender } { \l_@@_ref_language_tl } { g } { \l_@@_ref_gender_tl } \tl_clear:N \l_@@_ref_gender_tl } } } % \end{macrocode} % \end{macro} % % % % \subsection{Language files} % % Contrary to general options and type options, which are always \emph{local}, % language-specific settings are always \emph{global}. Hence, the loading of % built-in language files, as well as settings done with \cs{zcLanguageSetup}, % should set the relevant variables globally. % % The built-in language files and their related infrastructure are designed to % perform ``on the fly'' loading of the language files, ``lazily'' as needed. % Much like \pkg{babel} does for languages not declared in the preamble, but % used in the document. This offers some convenience, of course, and that's % one reason to do it. But it also has the purpose of parsimony, of ``loading % the least possible''. Therefore, we load at \texttt{begindocument} one % single language (see \zcref[ref=title, noname]{sec:lang-option}), as % specified by the user in the preamble with the \opt{lang} option or, failing % any specification, the current language of the document, which is the % default. Anything else is lazily loaded, on the fly, along the document. % % This design decision has also implications to the \emph{form} the language % files assumed. As far as my somewhat impressionistic sampling goes, % dictionary or localization files of the most common packages in this area of % functionality, are usually a set of commands which perform the relevant % definitions and assignments in the preamble or at \texttt{begindocument}. % This includes \pkg{translator}, \pkg{translations}, but also \pkg{babel}'s % \file{.ldf} files, and \pkg{biblatex}'s \file{.lbx} files. I'm not really % well acquainted with this machinery, but as far as I grasp, they all rely on % some variation of \cs{ProvidesFile} and \cs{input}. And they can be safely % \cs{input} without generating spurious content, because they rely on being % loaded before the document has actually started. As far as I can tell, % \pkg{babel}'s ``on the fly'' functionality is not based on the \file{.ldf} % files, but on the \file{.ini} files, and on \cs{babelprovide}. And the % \file{.ini} files are not in this form, but actually resemble % ``configuration files'' of sorts, which means they are read and processed % somehow else than with just \cs{input}. So we do the more or less the same % here. It seems a reasonable way to ensure we can load language files on the % fly robustly mid-document, without getting paranoid with the last bit of % white-space in them, and without introducing any undue content on the stream % when we cannot afford to do it. Hence, \pkg{zref-clever}'s built-in % language files are a set of \emph{key-value options} which are read from the % file, and fed to \texttt{\cs{keys_set:nn}\{zref-clever/langfile\}} by % \cs{@@_provide_langfile:n}. And they use the same syntax and options as % \cs{zcLanguageSetup} does. The language file itself is read with % \cs{ExplSyntaxOn} with the usual implications for white-space and catcodes. % % \cs{@@_provide_langfile:n} is only meant to load the built-in language % files. For languages declared by the user, or for any settings to a known % language made with \cs{zcLanguageSetup}, values are populated directly to a % variable \cs{g_@@_lang_\meta{language}_prop}. Hence, there is no need to % ``load'' anything in this case: definitions and assignments made by the user % are performed immediately. % % % \subsubsection*{Provide} % % \begin{macro}{\g_@@_loaded_langfiles_seq} % Used to keep track of whether a language file has already been loaded or % not. % \begin{macrocode} \seq_new:N \g_@@_loaded_langfiles_seq % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_load_langfile_verbose_bool} % Controls whether \cs{@@_provide_langfile:n} fails silently or verbosely % in case of unknown languages or not found language files. % \begin{macrocode} \bool_new:N \l_@@_load_langfile_verbose_bool % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_provide_langfile:n} % Load language file for known \meta{language} if it is available and if it % has not already been loaded. % \begin{syntax} % \cs{@@_provide_langfile:n} \Arg{language} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_provide_langfile:n #1 { \group_begin: \@bsphack \prop_get:NnNTF \g_@@_languages_prop {#1} \l_@@_base_language_tl { \seq_if_in:NVF \g_@@_loaded_langfiles_seq \l_@@_base_language_tl { \exp_args:Nx \file_get:nnNTF { zref-clever- \l_@@_base_language_tl .lang } { \ExplSyntaxOn } \l_tmpa_tl { \tl_clear:N \l_@@_setup_type_tl \exp_args:NNx \seq_set_from_clist:Nn \l_@@_lang_declension_seq { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { declension } } \seq_if_empty:NTF \l_@@_lang_declension_seq { \tl_clear:N \l_@@_lang_decl_case_tl } { \seq_get_left:NN \l_@@_lang_declension_seq \l_@@_lang_decl_case_tl } \exp_args:NNx \seq_set_from_clist:Nn \l_@@_lang_gender_seq { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { gender } } \keys_set:nV { zref-clever / langfile } \l_tmpa_tl \seq_gput_right:NV \g_@@_loaded_langfiles_seq \l_@@_base_language_tl \msg_note:nnx { zref-clever } { langfile-loaded } { \l_@@_base_language_tl } } { \bool_if:NT \l_@@_load_langfile_verbose_bool { \msg_warning:nnx { zref-clever } { langfile-not-available } { \l_@@_base_language_tl } } % \end{macrocode} % Even if we don't have the actual language file, we register it as % ``loaded''. At this point, it is a known language, properly declared. % There is no point in trying to load it multiple times, because users cannot % really provide the language files (well, technically they could, but we are % working so they don't need to, and have better ways to do what they want). % And if the users had provided some language-specific options themselves, by % means of \cs{zcLanguageSetup}, everything would be in place, and they could % use the \opt{lang} option multiple times, and the % \texttt{langfile-not-available} warning would never go away. % \begin{macrocode} \seq_gput_right:NV \g_@@_loaded_langfiles_seq \l_@@_base_language_tl } } } { \bool_if:NT \l_@@_load_langfile_verbose_bool { \msg_warning:nnn { zref-clever } { unknown-language-load } {#1} } } \@esphack \group_end: } \cs_generate_variant:Nn \@@_provide_langfile:n { x } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_provide_langfile_verbose:n} % Does the same as \cs{@@_provide_langfile:n}, but warns if the loading of % the language file has failed. % \begin{syntax} % \cs{@@_provide_langfile_verbose:n} \Arg{language} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_provide_langfile_verbose:n #1 { \group_begin: \bool_set_true:N \l_@@_load_langfile_verbose_bool \@@_provide_langfile:n {#1} \group_end: } \cs_generate_variant:Nn \@@_provide_langfile_verbose:n { x } % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \@@_provide_lang_opt_type:nn , % \@@_provide_lang_opt_default:nn , % } % A couple of auxiliary functions for the of % \texttt{{zref-clever/langfile}} keys set in % \cs{@@_provide_langfile:n}. They respectively ``provide'' (i.e. set if % it value does not exist, do nothing if it already does) ``type-specific'' % and ``default'' language options. Both receive \meta{key} and % \meta{value} as arguments, but \cs{@@_provide_lang_opt_type:nn} % relies on the current value of \cs{l_@@_setup_type_tl}, as set by the % \texttt{type} key. % \begin{syntax} % \cs{@@_provide_lang_opt_type:nn} \Arg{key} \Arg{value} % \cs{@@_provide_lang_opt_default:nn} \Arg{key} \Arg{value} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_provide_lang_opt_type:nn #1#2 { \exp_args:Nnx \prop_gput_if_new:cnn { g_@@_lang_ \l_@@_base_language_tl _prop } { type- \l_@@_setup_type_tl - #1 } {#2} } \cs_generate_variant:Nn \@@_provide_lang_opt_type:nn { nV } \cs_new_protected:Npn \@@_provide_lang_opt_default:nn #1#2 { \prop_gput_if_new:cnn { g_@@_lang_ \l_@@_base_language_tl _prop } { default- #1 } {#2} } \cs_generate_variant:Nn \@@_provide_lang_opt_default:nn { nV } % \end{macrocode} % \end{macro} % % % The set of keys for \texttt{{zref-clever/langfile}}, which is used to % process the language files in \cs{@@_provide_langfile:n}. The no-op cases % for each category have their messages sent to ``info''. These messages % should not occur, as long as the language files are well formed, but they're % placed there nevertheless, and can be leveraged in regression tests. % % \begin{macrocode} \keys_define:nn { zref-clever / langfile } { type .code:n = { \tl_if_empty:nTF {#1} { \tl_clear:N \l_@@_setup_type_tl } { \tl_set:Nn \l_@@_setup_type_tl {#1} } } , case .code:n = { \seq_if_empty:NTF \l_@@_lang_declension_seq { \msg_info:nnxx { zref-clever } { language-no-decl-setup } { \l_@@_base_language_tl } {#1} } { \seq_if_in:NnTF \l_@@_lang_declension_seq {#1} { \tl_set:Nn \l_@@_lang_decl_case_tl {#1} } { \msg_info:nnxx { zref-clever } { unknown-decl-case } {#1} { \l_@@_base_language_tl } \seq_get_left:NN \l_@@_lang_declension_seq \l_@@_lang_decl_case_tl } } } , case .value_required:n = true , gender .code:n = { \seq_if_empty:NTF \l_@@_lang_gender_seq { \msg_info:nnxxx { zref-clever } { language-no-gender } { \l_@@_base_language_tl } { gender } {#1} } { \tl_if_empty:NTF \l_@@_setup_type_tl { \msg_info:nnn { zref-clever } { option-only-type-specific } { gender } } { \clist_clear:N \l_tmpa_clist \clist_map_inline:nn {#1} { \seq_if_in:NnTF \l_@@_lang_gender_seq {##1} { \clist_put_right:Nn \l_tmpa_clist {##1} } { \msg_info:nnxx { zref-clever } { gender-not-declared } { \l_@@_base_language_tl } {##1} } } \clist_if_empty:NF \l_tmpa_clist { \exp_args:Nnx \@@_provide_lang_opt_type:nn { gender } { \clist_use:Nn \l_tmpa_clist { , } } } } } } , gender .value_required:n = true , cap .choices:nn = { true , false } { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_provide_lang_opt_default:nV { cap } \l_keys_choice_tl } { \@@_provide_lang_opt_type:nV { cap } \l_keys_choice_tl } } , cap .default:n = true , nocap .meta:n = { cap = false } , nocap .value_forbidden:n = true , abbrev .choices:nn = { true , false } { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_provide_lang_opt_default:nV { abbrev } \l_keys_choice_tl } { \@@_provide_lang_opt_type:nV { abbrev } \l_keys_choice_tl } } , abbrev .default:n = true , noabbrev .meta:n = { abbrev = false }, noabbrev .value_forbidden:n = true , } \seq_map_inline:Nn \c_@@_ref_options_necessarily_not_type_specific_seq { \keys_define:nn { zref-clever / langfile } { #1 .value_required:n = true , #1 .code:n = { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_provide_lang_opt_default:nn {#1} {##1} } { \msg_info:nnn { zref-clever } { option-not-type-specific } {#1} } } , } } \seq_map_inline:Nn \c_@@_ref_options_possibly_type_specific_seq { \keys_define:nn { zref-clever / langfile } { #1 .value_required:n = true , #1 .code:n = { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_provide_lang_opt_default:nn {#1} {##1} } { \@@_provide_lang_opt_type:nn {#1} {##1} } } , } } \seq_map_inline:Nn \c_@@_ref_options_type_names_seq { \keys_define:nn { zref-clever / langfile } { #1 .value_required:n = true , #1 .code:n = { \tl_if_empty:NTF \l_@@_setup_type_tl { \msg_info:nnn { zref-clever } { option-only-type-specific } {#1} } { \tl_if_empty:NTF \l_@@_lang_decl_case_tl { \@@_provide_lang_opt_type:nn {#1} {##1} } { \@@_provide_lang_opt_type:nn { \l_@@_lang_decl_case_tl - #1 } {##1} } } } , } } % \end{macrocode} % % % % \subsubsection*{Fallback} % % All ``strings'' queried with \cs{@@_get_ref_opt_typeset:nN} -- in practice, % those in either \cs{c_@@_ref_options_necessarily_not_type_specific_seq} or % \cs{c_@@_ref_options_possibly_type_specific_seq} -- must have their values % set for ``fallback'', even if to empty ones, since this is what will be % retrieved in the absence of a proper language-specific option, which will be % the case if \pkg{babel} or \pkg{polyglossia} is loaded and sets a language % which \pkg{zref-clever} does not know. On the other hand, ``type names'' % are not looked for in ``fallback'', since it is indeed impossible to provide % any reasonable value for them for a ``specified but unknown language''. % Also ``font'' options -- those in \cs{c_@@_ref_options_font_seq}, and % queried with \cs{@@_get_ref_opt_font:nN} -- do not need to be provided here, % since the later function sets an empty value if the option is not found. % % \begin{macrocode} \prop_new:N \g_@@_fallback_unknown_lang_prop \prop_gset_from_keyval:Nn \g_@@_fallback_unknown_lang_prop { tpairsep = {,~} , tlistsep = {,~} , tlastsep = {,~} , notesep = {~} , namesep = {\nobreakspace} , pairsep = {,~} , listsep = {,~} , lastsep = {,~} , rangesep = {\textendash} , preref = {} , postref = {} , } % \end{macrocode} % % % % \subsubsection*{Get language options} % % \begin{macro}{\@@_get_lang_opt_type:nnnNF} % Get type-specific language option of \meta{key} for \meta{type} and % \meta{language}, and store it in \meta{tl variable} if found. If not % found, leave the \meta{false code} on the stream, in which case the value % of \meta{tl variable} should not be relied upon. % \begin{syntax} % \cs{@@_get_lang_opt_type:nnnNF} \Arg{language} \Arg{type} \Arg{key} % ~~\meta{tl variable} \Arg{false code} % \end{syntax} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_get_lang_opt_type:nnnN #1#2#3#4 { F } { \prop_get:NnNTF \g_@@_languages_prop {#1} \l_@@_base_language_tl { \prop_get:cnNTF { g_@@_lang_ \l_@@_base_language_tl _prop } { type- #2 - #3 } #4 { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \@@_get_lang_opt_type:nnnN { xxxN , xxnN } { F } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_get_lang_opt_default:nnNF} % Get default language option of \meta{key} for \meta{language}, and store % it in \meta{tl variable} if found. If not found, leave the \meta{false % code} on the stream, in which case the value of \meta{tl variable} should % not be relied upon. % \begin{syntax} % \cs{@@_get_lang_opt_default:nnNF} \Arg{language} \Arg{key} % ~~\meta{tl variable} \Arg{false code} % \end{syntax} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_get_lang_opt_default:nnN #1#2#3 { F } { \prop_get:NnNTF \g_@@_languages_prop {#1} \l_@@_base_language_tl { \prop_get:cnNTF { g_@@_lang_ \l_@@_base_language_tl _prop } { default- #2 } #3 { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \@@_get_lang_opt_default:nnN { xnN } { F } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_get_fallback_unknown_lang_opt:nNF} % Get fallback language option of \meta{key}, and store it in \meta{tl % variable} if found. If not found, leave the \meta{false code} on the % stream, in which case the value of \meta{tl variable} should not be relied % upon. % \begin{syntax} % \cs{@@_get_fallback_unknown_lang_opt:nNF} \Arg{key} % ~~\meta{tl variable} \Arg{false code} % \end{syntax} % \begin{macrocode} % {} \prg_new_protected_conditional:Npnn \@@_get_fallback_unknown_lang_opt:nN #1#2 { F } { \prop_get:NnNTF \g_@@_fallback_unknown_lang_prop { #1 } #2 { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % % % \subsection{Options} % % % \subsubsection*{Auxiliary} % % % \begin{macro}{\@@_prop_put_non_empty:Nnn} % If \meta{value} is empty, remove \meta{key} from \meta{property list}. % Otherwise, add \meta{key} = \meta{value} to \meta{property list}. % \begin{syntax} % \cs{@@_prop_put_non_empty:Nnn} \meta{property list} \Arg{key} \Arg{value} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_prop_put_non_empty:Nnn #1#2#3 { \tl_if_empty:nTF {#3} { \prop_remove:Nn #1 {#2} } { \prop_put:Nnn #1 {#2} {#3} } } % \end{macrocode} % \end{macro} % % % \subsubsection*{\opt{ref} option} % % \cs{l_@@_ref_property_tl} stores the property to which the reference is % being made. Note that one thing \emph{must} be handled at this point: the % existence of the property itself, as far as \pkg{zref} is concerned. This % because typesetting relies on the check \cs{zref@ifrefcontainsprop}, which % \emph{presumes} the property is defined and silently expands the \emph{true} % branch if it is not (insightful comments by \contributor{Ulrike Fischer} at % \url{https://github.com/ho-tex/zref/issues/13}). Therefore, before adding % anything to \cs{l_@@_ref_property_tl}, check if first here with % \cs{zref@ifpropundefined}: close it at the door. % % % \begin{macrocode} \tl_new:N \l_@@_ref_property_tl \keys_define:nn { zref-clever / reference } { ref .code:n = { \zref@ifpropundefined {#1} { \msg_warning:nnn { zref-clever } { zref-property-undefined } {#1} \tl_set:Nn \l_@@_ref_property_tl { default } } { \tl_set:Nn \l_@@_ref_property_tl {#1} } } , ref .initial:n = default , ref .value_required:n = true , page .meta:n = { ref = page }, page .value_forbidden:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{typeset} option} % % \begin{macrocode} \bool_new:N \l_@@_typeset_ref_bool \bool_new:N \l_@@_typeset_name_bool \keys_define:nn { zref-clever / reference } { typeset .choice: , typeset / both .code:n = { \bool_set_true:N \l_@@_typeset_ref_bool \bool_set_true:N \l_@@_typeset_name_bool } , typeset / ref .code:n = { \bool_set_true:N \l_@@_typeset_ref_bool \bool_set_false:N \l_@@_typeset_name_bool } , typeset / name .code:n = { \bool_set_false:N \l_@@_typeset_ref_bool \bool_set_true:N \l_@@_typeset_name_bool } , typeset .initial:n = both , typeset .value_required:n = true , noname .meta:n = { typeset = ref } , noname .value_forbidden:n = true , noref .meta:n = { typeset = name } , noref .value_forbidden:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{sort} option} % % \begin{macrocode} \bool_new:N \l_@@_typeset_sort_bool \keys_define:nn { zref-clever / reference } { sort .bool_set:N = \l_@@_typeset_sort_bool , sort .initial:n = true , sort .default:n = true , nosort .meta:n = { sort = false }, nosort .value_forbidden:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{typesort} option} % % \cs{l_@@_typesort_seq} is stored reversed, since the sort priorities are % computed in the negative range in \cs{@@_sort_default_different_types:nn}, % so that we can implicitly rely on `0' being the ``last value'', and spare % creating an integer variable using \cs{seq_map_indexed_inline:Nn}. % % \begin{macrocode} \seq_new:N \l_@@_typesort_seq \keys_define:nn { zref-clever / reference } { typesort .code:n = { \seq_set_from_clist:Nn \l_@@_typesort_seq {#1} \seq_reverse:N \l_@@_typesort_seq } , typesort .initial:n = { part , chapter , section , paragraph }, typesort .value_required:n = true , notypesort .code:n = { \seq_clear:N \l_@@_typesort_seq } , notypesort .value_forbidden:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{comp} option} % % \begin{macrocode} \bool_new:N \l_@@_typeset_compress_bool \keys_define:nn { zref-clever / reference } { comp .bool_set:N = \l_@@_typeset_compress_bool , comp .initial:n = true , comp .default:n = true , nocomp .meta:n = { comp = false }, nocomp .value_forbidden:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{range} option} % % \begin{macrocode} \bool_new:N \l_@@_typeset_range_bool \keys_define:nn { zref-clever / reference } { range .bool_set:N = \l_@@_typeset_range_bool , range .initial:n = false , range .default:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{cap} and \opt{capfirst} options} % % \begin{macrocode} \bool_new:N \l_@@_capitalize_first_bool \keys_define:nn { zref-clever / reference } { cap .code:n = { \tl_if_empty:nTF {#1} { \prop_remove:Nn \l_@@_ref_options_prop { cap } } { \bool_lazy_or:nnTF { \str_if_eq_p:nn {#1} { true } } { \str_if_eq_p:nn {#1} { false } } { \prop_put:Nnn \l_@@_ref_options_prop { cap } {#1} } { \msg_warning:nnn { zref-clever } { key-boolean-or-empty } {#1} } } } , cap .default:n = true , nocap .meta:n = { cap = false } , nocap .value_forbidden:n = true , capfirst .bool_set:N = \l_@@_capitalize_first_bool , capfirst .initial:n = false , capfirst .default:n = true , } % \end{macrocode} % % % \subsubsection*{\opt{abbrev} and \opt{noabbrevfirst} options} % % \begin{macrocode} \bool_new:N \l_@@_noabbrev_first_bool \keys_define:nn { zref-clever / reference } { abbrev .code:n = { \tl_if_empty:nTF {#1} { \prop_remove:Nn \l_@@_ref_options_prop { abbrev } } { \bool_lazy_or:nnTF { \str_if_eq_p:nn {#1} { true } } { \str_if_eq_p:nn {#1} { false } } { \prop_put:Nnn \l_@@_ref_options_prop { abbrev } {#1} } { \msg_warning:nnn { zref-clever } { key-boolean-or-empty } {#1} } } } , abbrev .default:n = true , noabbrev .meta:n = { abbrev = false }, noabbrev .value_forbidden:n = true , noabbrevfirst .bool_set:N = \l_@@_noabbrev_first_bool , noabbrevfirst .initial:n = false , noabbrevfirst .default:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{S} option} % % \begin{macrocode} \keys_define:nn { zref-clever / reference } { S .meta:n = { capfirst = true , noabbrevfirst = true }, S .value_forbidden:n = true , } % \end{macrocode} % % % \subsubsection*{\opt{hyperref} option} % % \begin{macrocode} \bool_new:N \l_@@_use_hyperref_bool \bool_new:N \l_@@_warn_hyperref_bool \keys_define:nn { zref-clever / reference } { hyperref .choice: , hyperref / auto .code:n = { \bool_set_true:N \l_@@_use_hyperref_bool \bool_set_false:N \l_@@_warn_hyperref_bool } , hyperref / true .code:n = { \bool_set_true:N \l_@@_use_hyperref_bool \bool_set_true:N \l_@@_warn_hyperref_bool } , hyperref / false .code:n = { \bool_set_false:N \l_@@_use_hyperref_bool \bool_set_false:N \l_@@_warn_hyperref_bool } , hyperref .initial:n = auto , hyperref .default:n = auto } % \end{macrocode} % % \begin{macrocode} \AddToHook { begindocument } { \@@_if_package_loaded:nTF { hyperref } { \bool_if:NT \l_@@_use_hyperref_bool { \RequirePackage { zref-hyperref } } } { \bool_if:NT \l_@@_warn_hyperref_bool { \msg_warning:nn { zref-clever } { missing-hyperref } } \bool_set_false:N \l_@@_use_hyperref_bool } \keys_define:nn { zref-clever / reference } { hyperref .code:n = { \msg_warning:nn { zref-clever } { hyperref-preamble-only } } } } % \end{macrocode} % % % % \subsubsection*{\opt{nameinlink} option} % % \begin{macrocode} \str_new:N \l_@@_nameinlink_str \keys_define:nn { zref-clever / reference } { nameinlink .choice: , nameinlink / true .code:n = { \str_set:Nn \l_@@_nameinlink_str { true } } , nameinlink / false .code:n = { \str_set:Nn \l_@@_nameinlink_str { false } } , nameinlink / single .code:n = { \str_set:Nn \l_@@_nameinlink_str { single } } , nameinlink / tsingle .code:n = { \str_set:Nn \l_@@_nameinlink_str { tsingle } } , nameinlink .initial:n = tsingle , nameinlink .default:n = true , } % \end{macrocode} % % % \subsubsection*{\opt{preposinlink} option} % % \begin{macrocode} \bool_new:N \l_@@_preposinlink_bool \keys_define:nn { zref-clever / reference } { preposinlink .bool_set:N = \l_@@_preposinlink_bool , preposinlink .initial:n = false , preposinlink .default:n = true , } % \end{macrocode} % % % \subsubsection*{\opt{lang} option} % \phantomsection{}\zlabel{sec:lang-option} % % \cs{l_@@_current_language_tl} is an internal alias for \pkg{babel}'s % \cs{languagename} or \pkg{polyglossia}'s \cs{mainbabelname} and, if none of % them is loaded, we set it to \texttt{english}. \cs{l_@@_main_language_tl} % is an internal alias for \pkg{babel}'s \cs{bbl@main@language} or for % \pkg{polyglossia}'s \cs{mainbabelname}, as the case may be. Note that for % \pkg{polyglossia} we get \pkg{babel}'s language names, so that we only need % to handle those internally. \cs{l_@@_ref_language_tl} is the internal % variable which stores the language in which the reference is to be made. % % The overall setup here seems a little roundabout, but this is actually % required. In the preamble, we (potentially) don't yet have values for the % ``current'' and ``main'' document languages, this must be retrieved at a % \texttt{begindocument} hook. The \texttt{begindocument} hook is responsible % to get values for \cs{l_@@_current_language_tl} and % \cs{l_@@_main_language_tl}, and to set the default for % \cs{l_@@_ref_language_tl}. Package options, or preamble calls to % \cs{zcsetup} are also hooked at \texttt{begindocument}, but come after the % first hook, so that the pertinent variables have been set when they are % executed. Finally, we set a third \texttt{begindocument} hook, at % \texttt{begindocument/before}, so that it runs after any options set in the % preamble. This hook redefines the \opt{lang} option for immediate execution % in the document body, and ensures the \texttt{current} language's language % file gets loaded, if it hadn't been already. % % For the \pkg{babel} and \pkg{polyglossia} variables which store the % ``current'' and ``main'' languages, see % \url{https://tex.stackexchange.com/a/233178}, including comments, % particularly the one by Javier Bezos. For the \pkg{babel} and % \pkg{polyglossia} variables which store the list of loaded languages, see % \url{https://tex.stackexchange.com/a/281220}, including comments, % particularly PLK's. Note, however, that languages loaded by % \cs{babelprovide}, either directly, ``on the fly'', or with the % \texttt{provide} option, \texttt{do not} get included in \cs{bbl@loaded}. % % \begin{macrocode} \tl_new:N \l_@@_ref_language_tl \tl_new:N \l_@@_current_language_tl \tl_new:N \l_@@_main_language_tl \AddToHook { begindocument } { \@@_if_package_loaded:nTF { babel } { \tl_set:Nn \l_@@_current_language_tl { \languagename } \tl_set:Nn \l_@@_main_language_tl { \bbl@main@language } } { \@@_if_package_loaded:nTF { polyglossia } { \tl_set:Nn \l_@@_current_language_tl { \babelname } \tl_set:Nn \l_@@_main_language_tl { \mainbabelname } } { \tl_set:Nn \l_@@_current_language_tl { english } \tl_set:Nn \l_@@_main_language_tl { english } } } % \end{macrocode} % Provide default value for \cs{l_@@_ref_language_tl} corresponding to option % \opt{current}, but do so outside of the \pkg{l3keys} machinery (that is, % instead of using \texttt{.initial:n}), so that we are able to distinguish % when the user actually gave the option, in which case the language file % loading is done verbosely, from when we are setting the default value % (here), in which case the language file loading is done silently. % \begin{macrocode} \tl_set:Nn \l_@@_ref_language_tl { \l_@@_current_language_tl } } % \end{macrocode} % % % \begin{macrocode} \keys_define:nn { zref-clever / reference } { lang .code:n = { \AddToHook { begindocument } { \str_case:nnF {#1} { { current } { \tl_set:Nn \l_@@_ref_language_tl { \l_@@_current_language_tl } \@@_provide_langfile_verbose:x { \l_@@_ref_language_tl } } { main } { \tl_set:Nn \l_@@_ref_language_tl { \l_@@_main_language_tl } \@@_provide_langfile_verbose:x { \l_@@_ref_language_tl } } } { \prop_if_in:NnTF \g_@@_languages_prop {#1} { \tl_set:Nn \l_@@_ref_language_tl {#1} } { \msg_warning:nnn { zref-clever } { unknown-language-opt } {#1} \tl_set:Nn \l_@@_ref_language_tl { \l_@@_current_language_tl } } \@@_provide_langfile_verbose:x { \l_@@_ref_language_tl } } } } , lang .value_required:n = true , } % \end{macrocode} % % % \begin{macrocode} \AddToHook { begindocument / before } { \AddToHook { begindocument } { % \end{macrocode} % If any \opt{lang} option has been given by the user, the corresponding % language is already loaded, otherwise, ensure the default one % (\texttt{current}) gets loaded early, but not verbosely. % \begin{macrocode} \@@_provide_langfile:x { \l_@@_ref_language_tl } % \end{macrocode} % Redefinition of the \texttt{lang} key option for the document body. Also, % drop the verbose language file loading in the document body, as it can % become intrusive depending on the use case, and does not provide much % ``juice'' anyway: in \cs{zcref} missing names warnings will already ensue. % \begin{macrocode} \keys_define:nn { zref-clever / reference } { lang .code:n = { \str_case:nnF {#1} { { current } { \tl_set:Nn \l_@@_ref_language_tl { \l_@@_current_language_tl } \@@_provide_langfile:x { \l_@@_ref_language_tl } } { main } { \tl_set:Nn \l_@@_ref_language_tl { \l_@@_main_language_tl } \@@_provide_langfile:x { \l_@@_ref_language_tl } } } { \prop_if_in:NnTF \g_@@_languages_prop {#1} { \tl_set:Nn \l_@@_ref_language_tl {#1} } { \msg_warning:nnn { zref-clever } { unknown-language-opt } {#1} \tl_set:Nn \l_@@_ref_language_tl { \l_@@_current_language_tl } } \@@_provide_langfile:x { \l_@@_ref_language_tl } } } , lang .value_required:n = true , } } } % \end{macrocode} % % % % \subsubsection*{\opt{d} option} % % For setting the declension case. Short for convenience and for not % polluting the markup too much given that, for languages that need it, it may % get to be used frequently. % % \contributor{\texttt{@samcarter}} and \contributor{Alan Munn} provided % useful comments about declension on the TeX.SX chat. Also, % \contributor{Florent Rougon}'s efforts in this area, with the \pkg{xcref} % package (\url{https://github.com/frougon/xcref}), have been an insightful % source to frame the problem in general terms. % % \begin{macrocode} \tl_new:N \l_@@_ref_decl_case_tl \keys_define:nn { zref-clever / reference } { d .code:n = { \msg_warning:nnn { zref-clever } { option-document-only } { d } } , } \AddToHook { begindocument } { \keys_define:nn { zref-clever / reference } { % \end{macrocode} % We just store the value at this point, which is validated by % \cs{@@_process_language_options:} after \cs{keys_set:nn}. % \begin{macrocode} d .tl_set:N = \l_@@_ref_decl_case_tl , d .value_required:n = true , } } % \end{macrocode} % % % % \subsubsection*{\opt{nudge} \& co.\ options} % % \begin{macrocode} \bool_new:N \l_@@_nudge_enabled_bool \bool_new:N \l_@@_nudge_multitype_bool \bool_new:N \l_@@_nudge_comptosing_bool \bool_new:N \l_@@_nudge_singular_bool \bool_new:N \l_@@_nudge_gender_bool \tl_new:N \l_@@_ref_gender_tl \keys_define:nn { zref-clever / reference } { nudge .choice: , nudge / true .code:n = { \bool_set_true:N \l_@@_nudge_enabled_bool } , nudge / false .code:n = { \bool_set_false:N \l_@@_nudge_enabled_bool } , nudge / ifdraft .code:n = { \ifdraft { \bool_set_false:N \l_@@_nudge_enabled_bool } { \bool_set_true:N \l_@@_nudge_enabled_bool } } , nudge / iffinal .code:n = { \ifoptionfinal { \bool_set_true:N \l_@@_nudge_enabled_bool } { \bool_set_false:N \l_@@_nudge_enabled_bool } } , nudge .initial:n = false , nudge .default:n = true , nonudge .meta:n = { nudge = false } , nonudge .value_forbidden:n = true , nudgeif .code:n = { \bool_set_false:N \l_@@_nudge_multitype_bool \bool_set_false:N \l_@@_nudge_comptosing_bool \bool_set_false:N \l_@@_nudge_gender_bool \clist_map_inline:nn {#1} { \str_case:nnF {##1} { { multitype } { \bool_set_true:N \l_@@_nudge_multitype_bool } { comptosing } { \bool_set_true:N \l_@@_nudge_comptosing_bool } { gender } { \bool_set_true:N \l_@@_nudge_gender_bool } { all } { \bool_set_true:N \l_@@_nudge_multitype_bool \bool_set_true:N \l_@@_nudge_comptosing_bool \bool_set_true:N \l_@@_nudge_gender_bool } } { \msg_warning:nnn { zref-clever } { nudgeif-unknown-value } {##1} } } } , nudgeif .value_required:n = true , nudgeif .initial:n = all , sg .bool_set:N = \l_@@_nudge_singular_bool , sg .initial:n = false , sg .default:n = true , g .code:n = { \msg_warning:nnn { zref-clever } { option-document-only } { g } } , } \AddToHook { begindocument } { \keys_define:nn { zref-clever / reference } { % \end{macrocode} % We just store the value at this point, which is validated by % \cs{@@_process_language_options:} after \cs{keys_set:nn}. % \begin{macrocode} g .tl_set:N = \l_@@_ref_gender_tl , g .value_required:n = true , } } % \end{macrocode} % % % % \subsubsection*{\opt{font} option} % % \opt{font} \emph{can't be used as a package option}, since the options get % expanded by \LaTeX{} before being passed to the package (see % \url{https://tex.stackexchange.com/a/489570}). It can be set in \cs{zcref} % and, for global settings, with \cs{zcsetup}. Note that, technically, the % ``raw'' options are already available as \cs{@raw@opt@\meta{package}.sty} % (helpful comment by \contributor{David Carlisle} at % \url{https://tex.stackexchange.com/a/618439}). % % \begin{macrocode} \tl_new:N \l_@@_ref_typeset_font_tl \keys_define:nn { zref-clever / reference } { font .tl_set:N = \l_@@_ref_typeset_font_tl } % \end{macrocode} % % % % \subsubsection*{\opt{titleref} option} % % \begin{macrocode} \keys_define:nn { zref-clever / reference } { titleref .code:n = { \RequirePackage { zref-titleref } } , titleref .value_forbidden:n = true , } \AddToHook { begindocument } { \keys_define:nn { zref-clever / reference } { titleref .code:n = { \msg_warning:nn { zref-clever } { titleref-preamble-only } } } } % \end{macrocode} % % % \subsubsection*{\opt{note} option} % % \begin{macrocode} \tl_new:N \l_@@_zcref_note_tl \keys_define:nn { zref-clever / reference } { note .tl_set:N = \l_@@_zcref_note_tl , note .value_required:n = true , } % \end{macrocode} % % % \subsubsection*{\opt{check} option} % % Integration with \pkg{zref-check}. % % \begin{macrocode} \bool_new:N \l_@@_zrefcheck_available_bool \bool_new:N \l_@@_zcref_with_check_bool \keys_define:nn { zref-clever / reference } { check .code:n = { \RequirePackage { zref-check } } , check .value_forbidden:n = true , } \AddToHook { begindocument } { \@@_if_package_loaded:nTF { zref-check } { \bool_set_true:N \l_@@_zrefcheck_available_bool \keys_define:nn { zref-clever / reference } { check .code:n = { \bool_set_true:N \l_@@_zcref_with_check_bool \keys_set:nn { zref-check / zcheck } {#1} } , check .value_required:n = true , } } { \bool_set_false:N \l_@@_zrefcheck_available_bool \keys_define:nn { zref-clever / reference } { check .value_forbidden:n = false , check .code:n = { \msg_warning:nn { zref-clever } { missing-zref-check } } , } } } % \end{macrocode} % % % \subsubsection*{\opt{countertype} option} % % \cs{l_@@_counter_type_prop} is used by \texttt{zc@type} property, and stores % a mapping from ``counter'' to ``reference type''. Only those counters whose % type name is different from that of the counter need to be specified, since % \texttt{zc@type} presumes the counter as the type if the counter is not % found in \cs{l_@@_counter_type_prop}. % % \begin{macrocode} \prop_new:N \l_@@_counter_type_prop \keys_define:nn { zref-clever / label } { countertype .code:n = { \keyval_parse:nnn { \msg_warning:nnnn { zref-clever } { key-requires-value } { countertype } } { \@@_prop_put_non_empty:Nnn \l_@@_counter_type_prop } {#1} } , countertype .value_required:n = true , countertype .initial:n = { subsection = section , subsubsection = section , subparagraph = paragraph , enumi = item , enumii = item , enumiii = item , enumiv = item , mpfootnote = footnote , } , } % \end{macrocode} % % One interesting comment I received (by \contributor{Denis Bitouzé}, at % \githubissue{1}) about the most appropriate type for \texttt{paragraph} and % \texttt{subparagraph} counters was that the reader of the document does not % care whether that particular document structure element has been introduced % by \cs{paragraph} or, e.g.\ by the \cs{subsubsection} command. This is a % difference the author knows, as they're using \LaTeX{}, but to the reader % the difference between them is not really relevant, and it may be just % confusing to refer to them by different names. In this case the type for % \texttt{paragraph} and \texttt{subparagraph} should just be % \texttt{section}. I don't have a strong opinion about this, and the matter % was not pursued further. Besides, I presume not many people would set % \texttt{secnumdepth} so high to start with. But, for the time being, I left % the \texttt{paragraph} type for them, since there is actually a visual % difference to the reader between the \cs{subsubsection} and \cs{paragraph} % in the standard classes: up to the former, the sectioning commands break a % line before the following text, while, from the later on, the sectioning % commands and the following text are part of the same line. So, % \cs{paragraph} is actually different from ``just a shorter way to write % \cs{subsubsubsection}''. % % % \subsubsection*{\opt{counterresetters} option} % % \cs{l_@@_counter_resetters_seq} is used by \cs{@@_counter_reset_by:n} to % populate the \texttt{zc@enclval} property, and stores the list of counters % which are potential ``enclosing counters'' for other counters. This option % is constructed such that users can only \emph{add} items to the variable. % There would be little gain and some risk in allowing removal, and the syntax % of the option would become unnecessarily more complicated. Besides, users % can already override, for any particular counter, the search done from the % set in \cs{l_@@_counter_resetters_seq} with the \opt{counterresetby} option. % % \begin{macrocode} \seq_new:N \l_@@_counter_resetters_seq \keys_define:nn { zref-clever / label } { counterresetters .code:n = { \clist_map_inline:nn {#1} { \seq_if_in:NnF \l_@@_counter_resetters_seq {##1} { \seq_put_right:Nn \l_@@_counter_resetters_seq {##1} } } } , counterresetters .initial:n = { part , chapter , section , subsection , subsubsection , paragraph , subparagraph , }, counterresetters .value_required:n = true , } % \end{macrocode} % % % % \subsubsection*{\opt{counterresetby} option} % % \cs{l_@@_counter_resetby_prop} is used by \cs{@@_counter_reset_by:n} to % populate the \texttt{zc@enclval} property, and stores a mapping from % counters to the counter which resets each of them. This mapping has % precedence in \cs{@@_counter_reset_by:n} over the search through % \cs{l_@@_counter_resetters_seq}. % % \begin{macrocode} \prop_new:N \l_@@_counter_resetby_prop \keys_define:nn { zref-clever / label } { counterresetby .code:n = { \keyval_parse:nnn { \msg_warning:nnn { zref-clever } { key-requires-value } { counterresetby } } { \@@_prop_put_non_empty:Nnn \l_@@_counter_resetby_prop } {#1} } , counterresetby .value_required:n = true , counterresetby .initial:n = { % \end{macrocode} % The counters for the \texttt{enumerate} environment do not use the regular % counter machinery for resetting on each level, but are nested nevertheless % by other means, treat them as exception. % \begin{macrocode} enumii = enumi , enumiii = enumii , enumiv = enumiii , } , } % \end{macrocode} % % % \subsubsection*{\opt{currentcounter} option} % % \cs{l_@@_current_counter_tl} is pretty much the starting point of all of the % data specification for label setting done by \pkg{zref} with our setup for % it. It exists because we must provide some ``handle'' to specify the % current counter for packages/features that do not set \cs{@currentcounter} % appropriately. % % \begin{macrocode} \tl_new:N \l_@@_current_counter_tl \keys_define:nn { zref-clever / label } { currentcounter .tl_set:N = \l_@@_current_counter_tl , currentcounter .value_required:n = true , currentcounter .initial:n = \@currentcounter , } % \end{macrocode} % % % \subsubsection*{\opt{nocompat} option} % % % \begin{macrocode} \bool_new:N \g_@@_nocompat_bool \seq_new:N \g_@@_nocompat_modules_seq \keys_define:nn { zref-clever / reference } { nocompat .code:n = { \tl_if_empty:nTF {#1} { \bool_gset_true:N \g_@@_nocompat_bool } { \clist_map_inline:nn {#1} { \seq_if_in:NnF \g_@@_nocompat_modules_seq {##1} { \seq_gput_right:Nn \g_@@_nocompat_modules_seq {##1} } } } } , } \AddToHook { begindocument } { \keys_define:nn { zref-clever / reference } { nocompat .code:n = { \msg_warning:nnn { zref-clever } { option-preamble-only } { nocompat } } } } \AtEndOfPackage { \AddToHook { begindocument } { \seq_map_inline:Nn \g_@@_nocompat_modules_seq { \msg_warning:nnn { zref-clever } { unknown-compat-module } {#1} } } } % \end{macrocode} % % \begin{macro}{\@@_compat_module:nn} % Function to be used for compatibility modules loading. It should load the % module as long as \cs{l_@@_nocompat_bool} is false and \meta{module} is % not in \cs{l_@@_nocompat_modules_seq}. The \texttt{begindocument} hook is % needed so that we can have the option functional along the whole preamble, % not just at package load time. This requirement might be relaxed if we % made the option only available at load time, but this would not buy us % much leeway anyway, since for most compatibility modules, we must test for % the presence of packages at \texttt{begindocument}, only kernel features % and document classes could be checked reliably before that. Besides, % since we are using the new hook management system, there is always its % functionality to deal with potential loading order issues. % \begin{syntax} % \cs{@@_compat_module:nn} \Arg{module} \Arg{code} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_compat_module:nn #1#2 { \AddToHook { begindocument } { \bool_if:NF \g_@@_nocompat_bool { \seq_if_in:NnF \g_@@_nocompat_modules_seq {#1} {#2} } \seq_gremove_all:Nn \g_@@_nocompat_modules_seq {#1} } } % \end{macrocode} % \end{macro} % % % % \subsubsection*{Reference options} % \zlabel{sec:reference-options} % % This is a set of options related to reference typesetting which receive % equal treatment and, hence, are handled in batch. Since we are dealing with % options to be passed to \cs{zcref} or to \cs{zcsetup} or at load time, only % ``not necessarily type-specific'' options are pertinent here. However, they % \emph{may} either be type-specific or language-specific, and thus must be % stored in a property list, \cs{l_@@_ref_options_prop}, in order to be % retrieved from the option \emph{name} by \cs{@@_get_ref_opt_typeset:nN} and % \cs{@@_get_ref_opt_font:nN} according to context and precedence rules. % % The keys are set so that any value, including an empty one, is added to % \cs{l_@@_ref_options_prop}, while a key with \emph{no value} removes the % property from the list, so that these options can then fall back to lower % precedence levels settings. For discussion about the used technique, see % \zcref{sec:zcreftypesetup}. % % \begin{macrocode} \prop_new:N \l_@@_ref_options_prop \seq_map_inline:Nn \c_@@_ref_options_reference_seq { \keys_define:nn { zref-clever / reference } { #1 .default:V = \c_novalue_tl , #1 .code:n = { \tl_if_novalue:nTF {##1} { \prop_remove:Nn \l_@@_ref_options_prop {#1} } { \prop_put:Nnn \l_@@_ref_options_prop {#1} {##1} } } , } } \keys_define:nn { zref-clever / reference } { refpre .code:n = { % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha. \msg_warning:nnnn { zref-clever }{ option-deprecated } { refpre } { preref } } , refpos .code:n = { % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha. \msg_warning:nnnn { zref-clever }{ option-deprecated } { refpos } { postref } } , } % \end{macrocode} % % % % \subsubsection*{Package options} % % The options have been separated in two different groups, so that we can % potentially apply them selectively to different contexts: \texttt{label} and % \texttt{reference}. Currently, the only use of this selection is the % ability to exclude label related options from \cs{zcref}'s options. Anyway, % for load-time package options and for \cs{zcsetup} we want the whole set, so % we aggregate the two into \texttt{zref-clever/zcsetup}, and use that here. % % \begin{macrocode} \keys_define:nn { } { zref-clever / zcsetup .inherit:n = { zref-clever / label , zref-clever / reference , } } % \end{macrocode} % % % Process load-time package options % (\url{https://tex.stackexchange.com/a/15840}). % \begin{macrocode} \ProcessKeysOptions { zref-clever / zcsetup } % \end{macrocode} % % % % \section{Configuration} % % \subsection{\cs{zcsetup}} % % % \begin{macro}[int]{\zcsetup} % Provide \cs{zcsetup}. % \begin{syntax} % \cs{zcsetup}\marg{options} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcsetup { m } { \@@_zcsetup:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_zcsetup:n} % A version of \cs{zcsetup} for internal use with variant. % \begin{syntax} % \cs{@@_zcsetup:n}\marg{options} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_zcsetup:n #1 { \keys_set:nn { zref-clever / zcsetup } {#1} } \cs_generate_variant:Nn \@@_zcsetup:n { x } % \end{macrocode} % \end{macro} % % % % \subsection{\cs{zcRefTypeSetup}} % \zlabel{sec:zcreftypesetup} % % \cs{zcRefTypeSetup} is the main user interface for ``type-specific'' % reference formatting. Settings done by this command have a higher % precedence than any language-specific setting, either done at % \cs{zcLanguageSetup} or by the package's language files. On the other hand, % they have a lower precedence than non type-specific general options. The % \meta{options} should be given in the usual \texttt{key=val} format. The % \meta{type} does not need to pre-exist, the property list variable to store % the properties for the type gets created if need be. % % \begin{macro}[int]{\zcRefTypeSetup} % \begin{syntax} % \cs{zcRefTypeSetup} \marg{type} \marg{options} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcRefTypeSetup { m m } { \prop_if_exist:cF { l_@@_type_ #1 _options_prop } { \prop_new:c { l_@@_type_ #1 _options_prop } } \tl_set:Nn \l_@@_setup_type_tl {#1} \keys_set:nn { zref-clever / typesetup } {#2} } % \end{macrocode} % \end{macro} % % % Inside \cs{zcRefTypeSetup} any of the options \emph{can} receive empty % values, and those values, if they exist in the property list, will override % language-specific options, regardless of their emptiness. In principle, we % could live with the situation of, once a setting has been made in % \cs{l_@@_type__options_prop} or in \cs{l_@@_ref_options_prop} it stays % there forever, and can only be overridden by a new value at the same % precedence level or a higher one. But it would be nice if an user can % ``unset'' an option at either of those scopes to go back to the lower % precedence level of the language-specific options at any given point. So % both in \cs{zcRefTypeSetup} and in setting reference options (see % \zcref{sec:reference-options}), we leverage the distinction of an ``empty % valued key'' (\texttt{key=} or \texttt{key=\{\}}) from a ``key with no % value'' (\texttt{key}). This distinction is captured internally by the % lower-level key parsing, but must be made explicit at \cs{keys_set:nn} by % means of the \texttt{.default:V} property of the key in \cs{keys_define:nn}. % For the technique, by \contributor{Jonathan P.\ Spratte}, aka `Skillmon', % and some discussion about it, including further insights by % \contributor{Phelype Oleinik}, see % \url{https://tex.stackexchange.com/q/614690} and % \url{https://github.com/latex3/latex3/pull/988}. % % % \begin{macrocode} \keys_define:nn { zref-clever / typesetup } { cap .code:n = { \tl_if_empty:nTF {#1} { \prop_remove:cn { l_@@_type_ \l_@@_setup_type_tl _options_prop } { cap } } { \bool_lazy_or:nnTF { \str_if_eq_p:nn {#1} { true } } { \str_if_eq_p:nn {#1} { false } } { \prop_put:cnn { l_@@_type_ \l_@@_setup_type_tl _options_prop } { cap } {#1} } { \msg_warning:nnn { zref-clever } { key-boolean-or-empty } {#1} } } } , cap .default:n = true , nocap .meta:n = { cap = false } , nocap .value_forbidden:n = true , abbrev .code:n = { \tl_if_empty:nTF {#1} { \prop_remove:cn { l_@@_type_ \l_@@_setup_type_tl _options_prop } { abbrev } } { \bool_lazy_or:nnTF { \str_if_eq_p:nn {#1} { true } } { \str_if_eq_p:nn {#1} { false } } { \prop_put:cnn { l_@@_type_ \l_@@_setup_type_tl _options_prop } { abbrev } {#1} } { \msg_warning:nnn { zref-clever } { key-boolean-or-empty } {#1} } } } , abbrev .default:n = true , noabbrev .meta:n = { abbrev = false }, noabbrev .value_forbidden:n = true , } % \end{macrocode} % % % \begin{macrocode} \seq_map_inline:Nn \c_@@_ref_options_necessarily_not_type_specific_seq { \keys_define:nn { zref-clever / typesetup } { #1 .code:n = { \msg_warning:nnn { zref-clever } { option-not-type-specific } {#1} } , } } % \end{macrocode} % % % \begin{macrocode} \seq_map_inline:Nn \c_@@_ref_options_typesetup_seq { \keys_define:nn { zref-clever / typesetup } { #1 .default:V = \c_novalue_tl , #1 .code:n = { \tl_if_novalue:nTF {##1} { \prop_remove:cn { l_@@_type_ \l_@@_setup_type_tl _options_prop } {#1} } { \prop_put:cnn { l_@@_type_ \l_@@_setup_type_tl _options_prop } {#1} {##1} } } , } } \keys_define:nn { zref-clever / typesetup } { refpre .code:n = { % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha. \msg_warning:nnnn { zref-clever }{ option-deprecated } { refpre } { preref } } , refpos .code:n = { % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha. \msg_warning:nnnn { zref-clever }{ option-deprecated } { refpos } { postref } } , } % \end{macrocode} % % % % \subsection{\cs{zcLanguageSetup}} % % \cs{zcLanguageSetup} is the main user interface for ``language-specific'' % reference formatting, be it ``type-specific'' or not. The difference % between the two cases is captured by the \texttt{type} key, which works as a % sort of a ``switch''. Inside the \meta{options} argument of % \cs{zcLanguageSetup}, any options made before the first \texttt{type} key % declare ``default'' (non type-specific) language options. When the % \texttt{type} key is given with a value, the options following it will set % ``type-specific'' language options for that type. The current type can be % switched off by an empty \texttt{type} key. \cs{zcLanguageSetup} is % preamble only. % % \begin{macro}[int]{\zcLanguageSetup} % \begin{syntax} % \cs{zcLanguageSetup}\marg{language}\marg{options} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcLanguageSetup { m m } { \group_begin: \prop_get:NnNTF \g_@@_languages_prop {#1} \l_@@_base_language_tl { \tl_clear:N \l_@@_setup_type_tl \exp_args:NNx \seq_set_from_clist:Nn \l_@@_lang_declension_seq { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { declension } } \seq_if_empty:NTF \l_@@_lang_declension_seq { \tl_clear:N \l_@@_lang_decl_case_tl } { \seq_get_left:NN \l_@@_lang_declension_seq \l_@@_lang_decl_case_tl } \exp_args:NNx \seq_set_from_clist:Nn \l_@@_lang_gender_seq { \prop_item:cn { g_@@_lang_ \l_@@_base_language_tl _prop } { gender } } \keys_set:nn { zref-clever / langsetup } {#2} } { \msg_warning:nnn { zref-clever } { unknown-language-setup } {#1} } \group_end: } \@onlypreamble \zcLanguageSetup % \end{macrocode} % \end{macro} % % % \begin{macro} % { % \@@_declare_lang_opt_type:nnnn , % \@@_declare_lang_opt_default:nnn , % } % A couple of auxiliary functions for the of % \texttt{{zref-clever/langsetup}} keys set in \cs{zcLanguageSetup}. They % respectively declare (unconditionally set) ``type-specific'' and % ``default'' language-specific options. % \begin{syntax} % \cs{@@_declare_lang_opt_type:nnnn} \Arg{language} \Arg{type} % ~~\Arg{key} \Arg{value} % \cs{@@_declare_lang_opt_default:nnn} \Arg{language} % ~~\Arg{key} \Arg{value} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_declare_lang_opt_type:nnnn #1#2#3#4 { \prop_gput:cnn { g_@@_lang_ #1 _prop } { type- #2 - #3 } {#4} } \cs_generate_variant:Nn \@@_declare_lang_opt_type:nnnn { VVnn , VVxn , VVnx , VVnV } \cs_new_protected:Npn \@@_declare_lang_opt_default:nnn #1#2#3 { \prop_gput:cnn { g_@@_lang_ #1 _prop } { default- #2 } {#3} } \cs_generate_variant:Nn \@@_declare_lang_opt_default:nnn { Vnn , VnV } % \end{macrocode} % \end{macro} % % % The set of keys for \texttt{{zref-clever/langsetup}}, which is used to set % language-specific options in \cs{zcLanguageSetup}. % % \begin{macrocode} \keys_define:nn { zref-clever / langsetup } { type .code:n = { \tl_if_empty:nTF {#1} { \tl_clear:N \l_@@_setup_type_tl } { \tl_set:Nn \l_@@_setup_type_tl {#1} } } , case .code:n = { \seq_if_empty:NTF \l_@@_lang_declension_seq { \msg_warning:nnxx { zref-clever } { language-no-decl-setup } { \l_@@_base_language_tl } {#1} } { \seq_if_in:NnTF \l_@@_lang_declension_seq {#1} { \tl_set:Nn \l_@@_lang_decl_case_tl {#1} } { \msg_warning:nnxx { zref-clever } { unknown-decl-case } {#1} { \l_@@_base_language_tl } \seq_get_left:NN \l_@@_lang_declension_seq \l_@@_lang_decl_case_tl } } } , case .value_required:n = true , gender .code:n = { \seq_if_empty:NTF \l_@@_lang_gender_seq { \msg_warning:nnxxx { zref-clever } { language-no-gender } { \l_@@_base_language_tl } { gender } {#1} } { \tl_if_empty:NTF \l_@@_setup_type_tl { \msg_warning:nnn { zref-clever } { option-only-type-specific } { gender } } { \clist_clear:N \l_tmpa_clist \clist_map_inline:nn {#1} { \seq_if_in:NnTF \l_@@_lang_gender_seq {##1} { \clist_put_right:Nn \l_tmpa_clist {##1} } { \msg_warning:nnxx { zref-clever } { gender-not-declared } { \l_@@_base_language_tl } {##1} } } \clist_if_empty:NF \l_tmpa_clist { \@@_declare_lang_opt_type:VVnx \l_@@_base_language_tl \l_@@_setup_type_tl { gender } { \clist_use:Nn \l_tmpa_clist { , } } } } } } , gender .value_required:n = true , cap .choices:nn = { true , false } { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_declare_lang_opt_default:VnV \l_@@_base_language_tl { cap } \l_keys_choice_tl } { \@@_declare_lang_opt_type:VVnV \l_@@_base_language_tl \l_@@_setup_type_tl { cap } \l_keys_choice_tl } } , cap .default:n = true , nocap .meta:n = { cap = false } , nocap .value_forbidden:n = true , abbrev .choices:nn = { true , false } { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_declare_lang_opt_default:VnV \l_@@_base_language_tl { abbrev } \l_keys_choice_tl } { \@@_declare_lang_opt_type:VVnV \l_@@_base_language_tl \l_@@_setup_type_tl { abbrev } \l_keys_choice_tl } } , abbrev .default:n = true , noabbrev .meta:n = { abbrev = false }, noabbrev .value_forbidden:n = true , } \seq_map_inline:Nn \c_@@_ref_options_necessarily_not_type_specific_seq { \keys_define:nn { zref-clever / langsetup } { #1 .value_required:n = true , #1 .code:n = { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_declare_lang_opt_default:Vnn \l_@@_base_language_tl {#1} {##1} } { \msg_warning:nnn { zref-clever } { option-not-type-specific } {#1} } } , } } \seq_map_inline:Nn \c_@@_ref_options_possibly_type_specific_seq { \keys_define:nn { zref-clever / langsetup } { #1 .value_required:n = true , #1 .code:n = { \tl_if_empty:NTF \l_@@_setup_type_tl { \@@_declare_lang_opt_default:Vnn \l_@@_base_language_tl {#1} {##1} } { \@@_declare_lang_opt_type:VVnn \l_@@_base_language_tl \l_@@_setup_type_tl {#1} {##1} } } , } } \keys_define:nn { zref-clever / langsetup } { refpre .code:n = { % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha. \msg_warning:nnnn { zref-clever }{ option-deprecated } { refpre } { preref } } , refpos .code:n = { % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha. \msg_warning:nnnn { zref-clever }{ option-deprecated } { refpos } { postref } } , } \seq_map_inline:Nn \c_@@_ref_options_type_names_seq { \keys_define:nn { zref-clever / langsetup } { #1 .value_required:n = true , #1 .code:n = { \tl_if_empty:NTF \l_@@_setup_type_tl { \msg_warning:nnn { zref-clever } { option-only-type-specific } {#1} } { \tl_if_empty:NTF \l_@@_lang_decl_case_tl { \@@_declare_lang_opt_type:VVnn \l_@@_base_language_tl \l_@@_setup_type_tl {#1} {##1} } { \@@_declare_lang_opt_type:VVxn \l_@@_base_language_tl \l_@@_setup_type_tl { \l_@@_lang_decl_case_tl - #1 } {##1} } } } , } } % \end{macrocode} % % % % \section{User interface} % % \subsection{\cs{zcref}} % % % \begin{macro}[int]{\zcref} % The main user command of the package. % \begin{syntax} % \cs{zcref}\meta{*}\oarg{options}\marg{labels} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcref { s O { } m } { \zref@wrapper@babel \@@_zcref:nnn {#3} {#1} {#2} } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_zcref:nnnn} % An intermediate internal function, which does the actual heavy lifting, % and places \Arg{labels} as first argument, so that it can be protected by % \cs{zref@wrapper@babel} in \cs{zcref}. % \begin{syntax} % \cs{@@_zcref:nnnn} \Arg{labels} \Arg{*} \Arg{options} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_zcref:nnn #1#2#3 { \group_begin: % \end{macrocode} % Set options. % \begin{macrocode} \keys_set:nn { zref-clever / reference } {#3} % \end{macrocode} % Store arguments values. % \begin{macrocode} \seq_set_from_clist:Nn \l_@@_zcref_labels_seq {#1} \bool_set:Nn \l_@@_link_star_bool {#2} % \end{macrocode} % Ensure language file for reference language is loaded, if available. We % cannot rely on \cs{keys_set:nn} for the task, since if the \opt{lang} option % is set for \texttt{current}, the actual language may have changed outside % our control. \cs{@@_provide_langfile:x} does nothing if the language file % is already loaded. % \begin{macrocode} \@@_provide_langfile:x { \l_@@_ref_language_tl } % \end{macrocode} % Process \cs{zcDeclareLanguage} options. % \begin{macrocode} \@@_process_language_options: % \end{macrocode} % Integration with \pkg{zref-check}. % \begin{macrocode} \bool_lazy_and:nnT { \l_@@_zrefcheck_available_bool } { \l_@@_zcref_with_check_bool } { \zrefcheck_zcref_beg_label: } % \end{macrocode} % Sort the labels. % \begin{macrocode} \bool_lazy_or:nnT { \l_@@_typeset_sort_bool } { \l_@@_typeset_range_bool } { \@@_sort_labels: } % \end{macrocode} % Typeset the references. Also, set the reference font, and group it, so that % it does not leak to the note. % \begin{macrocode} \group_begin: \l_@@_ref_typeset_font_tl \@@_typeset_refs: \group_end: % \end{macrocode} % Typeset \texttt{note}. % \begin{macrocode} \tl_if_empty:NF \l_@@_zcref_note_tl { \@@_get_ref_opt_typeset:nN { notesep } \l_tmpa_tl \l_tmpa_tl \l_@@_zcref_note_tl } % \end{macrocode} % Integration with \pkg{zref-check}. % \begin{macrocode} \bool_lazy_and:nnT { \l_@@_zrefcheck_available_bool } { \l_@@_zcref_with_check_bool } { \zrefcheck_zcref_end_label_maybe: \zrefcheck_zcref_run_checks_on_labels:n { \l_@@_zcref_labels_seq } } % \end{macrocode} % Integration with \pkg{mathtools}. % \begin{macrocode} \bool_if:NT \l_@@_mathtools_showonlyrefs_bool { \@@_mathtools_showonlyrefs:n { \l_@@_zcref_labels_seq } } \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_zcref_labels_seq, \l_@@_link_star_bool} % \begin{macrocode} \seq_new:N \l_@@_zcref_labels_seq \bool_new:N \l_@@_link_star_bool % \end{macrocode} % \end{macro} % % % % \subsection{\cs{zcpageref}} % % % \begin{macro}[int]{\zcpageref} % A \cs{pageref} equivalent of \cs{zcref}. % \begin{syntax} % \cs{zcpageref}\meta{*}\oarg{options}\marg{labels} % \end{syntax} % \begin{macrocode} \NewDocumentCommand \zcpageref { s O { } m } { \IfBooleanTF {#1} { \zcref*[#2, ref = page] {#3} } { \zcref [#2, ref = page] {#3} } } % \end{macrocode} % \end{macro} % % % % \section{Sorting} % % Sorting is certainly a ``big task'' for \pkg{zref-clever} but, in the end, % it boils down to ``carefully done branching'', and quite some of it. The % sorting of ``page'' references is very much lightened by the availability of % \texttt{abspage}, from the \pkg{zref-abspage} module, which offers ``just % what we need'' for our purposes. The sorting of ``default'' references % falls on two main cases: i) labels of the same type; ii) labels of different % types. The first case is sorted according to the priorities set by the % \opt{typesort} option or, if that is silent for the case, by the order in % which labels were given by the user in \cs{zcref}. The second case is the % most involved one, since it is possible for multiple counters to be bundled % together in a single reference type. Because of this, sorting must take % into account the whole chain of ``enclosing counters'' for the counters of % the labels at hand. % % \begin{macro} % { % \l_@@_label_type_a_tl , % \l_@@_label_type_b_tl , % \l_@@_label_enclval_a_tl , % \l_@@_label_enclval_b_tl , % \l_@@_label_extdoc_a_tl , % \l_@@_label_extdoc_b_tl , % } % Auxiliary variables, for use in sorting, and some also in typesetting. % Used to store reference information -- label properties -- of the % ``current'' (\texttt{a}) and ``next'' (\texttt{b}) labels. % \begin{macrocode} \tl_new:N \l_@@_label_type_a_tl \tl_new:N \l_@@_label_type_b_tl \tl_new:N \l_@@_label_enclval_a_tl \tl_new:N \l_@@_label_enclval_b_tl \tl_new:N \l_@@_label_extdoc_a_tl \tl_new:N \l_@@_label_extdoc_b_tl % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_sort_decided_bool} % Auxiliary variable for \cs{@@_sort_default_same_type:nn}, signals if the % sorting between two labels has been decided or not. % \begin{macrocode} \bool_new:N \l_@@_sort_decided_bool % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_sort_prior_a_int,\l_@@_sort_prior_b_int} % Auxiliary variables for \cs{@@_sort_default_different_types:nn}. Store % the sort priority of the ``current'' and ``next'' labels. % \begin{macrocode} \int_new:N \l_@@_sort_prior_a_int \int_new:N \l_@@_sort_prior_b_int % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_label_types_seq} % Stores the order in which reference types appear in the label list % supplied by the user in \cs{zcref}. This variable is populated by % \cs{@@_label_type_put_new_right:n} at the start of \cs{@@_sort_labels:}. % This order is required as a ``last resort'' sort criterion between the % reference types, for use in \cs{@@_sort_default_different_types:nn}. % \begin{macrocode} \seq_new:N \l_@@_label_types_seq % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sort_labels:} % The main sorting function. It does not receive arguments, but it is % expected to be run inside \cs{@@_zcref:nnnn} where a number of environment % variables are to be set appropriately. In particular, % \cs{l_@@_zcref_labels_seq} should contain the labels received as argument % to \cs{zcref}, and the function performs its task by sorting this % variable. % \begin{macrocode} \cs_new_protected:Npn \@@_sort_labels: { % \end{macrocode} % Store label types sequence. % \begin{macrocode} \seq_clear:N \l_@@_label_types_seq \tl_if_eq:NnF \l_@@_ref_property_tl { page } { \seq_map_function:NN \l_@@_zcref_labels_seq \@@_label_type_put_new_right:n } % \end{macrocode} % Sort. % \begin{macrocode} \seq_sort:Nn \l_@@_zcref_labels_seq { \zref@ifrefundefined {##1} { \zref@ifrefundefined {##2} { % Neither label is defined. \sort_return_same: } { % The second label is defined, but the first isn't, leave the % undefined first (to be more visible). \sort_return_same: } } { \zref@ifrefundefined {##2} { % The first label is defined, but the second isn't, bring the % second forward. \sort_return_swapped: } { % The interesting case: both labels are defined. References % to the "default" property or to the "page" are quite % different with regard to sorting, so we branch them here to % specialized functions. \tl_if_eq:NnTF \l_@@_ref_property_tl { page } { \@@_sort_page:nn {##1} {##2} } { \@@_sort_default:nn {##1} {##2} } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_label_type_put_new_right:n} % Auxiliary function used to store the order in which reference types appear % in the label list supplied by the user in \cs{zcref}. It is expected to % be run inside \cs{@@_sort_labels:}, and stores the types sequence in % \cs{l_@@_label_types_seq}. I have tried to handle the same task inside % \cs{seq_sort:Nn} in \cs{@@_sort_labels:} to spare mapping over % \cs{l_@@_zcref_labels_seq}, but it turned out it not to be easy to rely on % the order the labels get processed at that point, since the variable is % being sorted there. Besides, the mapping is simple, not a particularly % expensive operation. Anyway, this keeps things clean. % \begin{syntax} % \cs{@@_label_type_put_new_right:n} \Arg{label} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_label_type_put_new_right:n #1 { \@@_extract_default:Nnnn \l_@@_label_type_a_tl {#1} { zc@type } { \c_empty_tl } \seq_if_in:NVF \l_@@_label_types_seq \l_@@_label_type_a_tl { \seq_put_right:NV \l_@@_label_types_seq \l_@@_label_type_a_tl } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sort_default:nn} % The heavy-lifting function for sorting of defined labels for ``default'' % references (that is, a standard reference, not to ``page''). This % function is expected to be called within the sorting loop of % \cs{@@_sort_labels:} and receives the pair of labels being considered for % a change of order or not. It should \emph{always} ``return'' either % \cs{sort_return_same:} or \cs{sort_return_swapped:}. % \begin{syntax} % \cs{@@_sort_default:nn} \Arg{label a} \Arg{label b} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_sort_default:nn #1#2 { \@@_extract_default:Nnnn \l_@@_label_type_a_tl {#1} { zc@type } { zc@missingtype } \@@_extract_default:Nnnn \l_@@_label_type_b_tl {#2} { zc@type } { zc@missingtype } \tl_if_eq:NNTF \l_@@_label_type_a_tl \l_@@_label_type_b_tl { \@@_sort_default_same_type:nn {#1} {#2} } { \@@_sort_default_different_types:nn {#1} {#2} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sort_default_same_type:nn} % \begin{syntax} % \cs{@@_sort_default_same_type:nn} \Arg{label a} \Arg{label b} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_sort_default_same_type:nn #1#2 { \@@_extract_default:Nnnn \l_@@_label_enclval_a_tl {#1} { zc@enclval } { \c_empty_tl } \tl_reverse:N \l_@@_label_enclval_a_tl \@@_extract_default:Nnnn \l_@@_label_enclval_b_tl {#2} { zc@enclval } { \c_empty_tl } \tl_reverse:N \l_@@_label_enclval_b_tl \@@_extract_default:Nnnn \l_@@_label_extdoc_a_tl {#1} { externaldocument } { \c_empty_tl } \@@_extract_default:Nnnn \l_@@_label_extdoc_b_tl {#2} { externaldocument } { \c_empty_tl } \bool_set_false:N \l_@@_sort_decided_bool % First we check if there's any "external document" difference (coming % from 'zref-xr') and, if so, sort based on that. \tl_if_eq:NNF \l_@@_label_extdoc_a_tl \l_@@_label_extdoc_b_tl { \bool_if:nTF { \tl_if_empty_p:V \l_@@_label_extdoc_a_tl && ! \tl_if_empty_p:V \l_@@_label_extdoc_b_tl } { \bool_set_true:N \l_@@_sort_decided_bool \sort_return_same: } { \bool_if:nTF { ! \tl_if_empty_p:V \l_@@_label_extdoc_a_tl && \tl_if_empty_p:V \l_@@_label_extdoc_b_tl } { \bool_set_true:N \l_@@_sort_decided_bool \sort_return_swapped: } { \bool_set_true:N \l_@@_sort_decided_bool % Two different "external documents": last resort, sort by the % document name itself. \str_compare:eNeTF { \l_@@_label_extdoc_b_tl } < { \l_@@_label_extdoc_a_tl } { \sort_return_swapped: } { \sort_return_same: } } } } \bool_until_do:Nn \l_@@_sort_decided_bool { \bool_if:nTF { % Both are empty: neither label has any (further) "enclosing % counters" (left). \tl_if_empty_p:V \l_@@_label_enclval_a_tl && \tl_if_empty_p:V \l_@@_label_enclval_b_tl } { \bool_set_true:N \l_@@_sort_decided_bool \int_compare:nNnTF { \@@_extract:nnn {#1} { zc@cntval } { -1 } } > { \@@_extract:nnn {#2} { zc@cntval } { -1 } } { \sort_return_swapped: } { \sort_return_same: } } { \bool_if:nTF { % `a' is empty (and `b' is not): `b' may be nested in `a'. \tl_if_empty_p:V \l_@@_label_enclval_a_tl } { \bool_set_true:N \l_@@_sort_decided_bool \int_compare:nNnTF { \@@_extract:nnn {#1} { zc@cntval } { } } > { \tl_head:N \l_@@_label_enclval_b_tl } { \sort_return_swapped: } { \sort_return_same: } } { \bool_if:nTF { % `b' is empty (and `a' is not): `a' may be nested in `b'. \tl_if_empty_p:V \l_@@_label_enclval_b_tl } { \bool_set_true:N \l_@@_sort_decided_bool \int_compare:nNnTF { \tl_head:N \l_@@_label_enclval_a_tl } < { \@@_extract:nnn {#2} { zc@cntval } { } } { \sort_return_same: } { \sort_return_swapped: } } { % Neither is empty: we can compare the values of the % current enclosing counter in the loop, if they are % equal, we are still in the loop, if they are not, a % sorting decision can be made directly. \int_compare:nNnTF { \tl_head:N \l_@@_label_enclval_a_tl } = { \tl_head:N \l_@@_label_enclval_b_tl } { \tl_set:Nx \l_@@_label_enclval_a_tl { \tl_tail:N \l_@@_label_enclval_a_tl } \tl_set:Nx \l_@@_label_enclval_b_tl { \tl_tail:N \l_@@_label_enclval_b_tl } } { \bool_set_true:N \l_@@_sort_decided_bool \int_compare:nNnTF { \tl_head:N \l_@@_label_enclval_a_tl } > { \tl_head:N \l_@@_label_enclval_b_tl } { \sort_return_swapped: } { \sort_return_same: } } } } } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sort_default_different_types:nn} % \begin{syntax} % \cs{@@_sort_default_different_types:nn} \Arg{label a} \Arg{label b} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_sort_default_different_types:nn #1#2 { % \end{macrocode} % Retrieve sort priorities for \meta{label a} and \meta{label b}. % \cs{l_@@_typesort_seq} was stored in reverse sequence, and we compute the % sort priorities in the negative range, so that we can implicitly rely on `0' % being the ``last value''. % \begin{macrocode} \int_zero:N \l_@@_sort_prior_a_int \int_zero:N \l_@@_sort_prior_b_int \seq_map_indexed_inline:Nn \l_@@_typesort_seq { \tl_if_eq:nnTF {##2} {{othertypes}} { \int_compare:nNnT { \l_@@_sort_prior_a_int } = { 0 } { \int_set:Nn \l_@@_sort_prior_a_int { - ##1 } } \int_compare:nNnT { \l_@@_sort_prior_b_int } = { 0 } { \int_set:Nn \l_@@_sort_prior_b_int { - ##1 } } } { \tl_if_eq:NnTF \l_@@_label_type_a_tl {##2} { \int_set:Nn \l_@@_sort_prior_a_int { - ##1 } } { \tl_if_eq:NnT \l_@@_label_type_b_tl {##2} { \int_set:Nn \l_@@_sort_prior_b_int { - ##1 } } } } } % \end{macrocode} % Then do the actual sorting. % \begin{macrocode} \bool_if:nTF { \int_compare_p:nNn { \l_@@_sort_prior_a_int } < { \l_@@_sort_prior_b_int } } { \sort_return_same: } { \bool_if:nTF { \int_compare_p:nNn { \l_@@_sort_prior_a_int } > { \l_@@_sort_prior_b_int } } { \sort_return_swapped: } { % Sort priorities are equal: the type that occurs first in % `labels', as given by the user, is kept (or brought) forward. \seq_map_inline:Nn \l_@@_label_types_seq { \tl_if_eq:NnTF \l_@@_label_type_a_tl {##1} { \seq_map_break:n { \sort_return_same: } } { \tl_if_eq:NnT \l_@@_label_type_b_tl {##1} { \seq_map_break:n { \sort_return_swapped: } } } } } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sort_page:nn} % The sorting function for sorting of defined labels for references to % ``page''. This function is expected to be called within the sorting loop % of \cs{@@_sort_labels:} and receives the pair of labels being considered % for a change of order or not. It should \emph{always} ``return'' either % \cs{sort_return_same:} or \cs{sort_return_swapped:}. Compared to the % sorting of default labels, this is a piece of cake (thanks to % \texttt{abspage}). % \begin{syntax} % \cs{@@_sort_page:nn} \Arg{label a} \Arg{label b} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_sort_page:nn #1#2 { \int_compare:nNnTF { \@@_extract:nnn {#1} { abspage } { -1 } } > { \@@_extract:nnn {#2} { abspage } { -1 } } { \sort_return_swapped: } { \sort_return_same: } } % \end{macrocode} % \end{macro} % % % % \section{Typesetting} % % ``Typesetting'' the reference, which here includes the parsing of the labels % and eventual compression of labels in sequence into ranges, is definitely % the ``crux'' of \pkg{zref-clever}. This because we process the label set as % a stack, in a single pass, and hence ``parsing'', ``compressing'', and % ``typesetting'' must be decided upon at the same time, making it difficult % to slice the job into more specific and self-contained tasks. So, do bear % this in mind before you curse me for the length of some of the functions % below, or before a more orthodox ``docstripper'' complains about me not % sticking to code commenting conventions to keep the code more readable in % the \file{.dtx} file. % % While processing the label stack (kept in \cs{l_@@_typeset_labels_seq}), % \cs{@@_typeset_refs:} ``sees'' two labels, and two labels only, the % ``current'' one (kept in \cs{l_@@_label_a_tl}), and the ``next'' one (kept % in \cs{l_@@_label_b_tl}). However, the typesetting needs (a lot) more % information than just these two immediate labels to make a number of % critical decisions. Some examples: i) We cannot know if labels ``current'' % and ``next'' of the same type are a ``pair'', or just ``elements in a % list'', until we examine the label after ``next''; ii) If the ``next'' label % is of the same type as the ``current'', and it is in immediate sequence to % it, it potentially forms a ``range'', but we cannot know if ``next'' is % actually the end of the range until we examined an arbitrary number of % labels, and found one which is not in sequence from the previous one; iii) % When processing a type block, the ``name'' comes first, however, we only % know if that name should be plural, or if it should be included in the % hyperlink, after processing an arbitrary number of labels and find one of a % different type. One could naively assume that just examining ``next'' would % be enough for this, since we can know if it is of the same type or not. % Alas, ``there be ranges'', and a compression operation may boil down to a % single element, so we have to process the whole type block to know how its % name should be typeset; iv) Similar issues apply to lists of type blocks, % each of which is of arbitrary length: we can only know if two type blocks % form a ``pair'' or are ``elements in a list'' when we finish the % block. Etc.\ etc.\ etc. % % We handle this by storing the reference ``pieces'' in ``queues'', instead of % typesetting them immediately upon processing. The ``queues'' get typeset at % the point where all the information needed is available, which usually % happens when a type block finishes (we see something of a different type in % ``next'', signaled by \cs{l_@@_last_of_type_bool}), or the stack itself % finishes (has no more elements, signaled by \cs{l_@@_typeset_last_bool}). % And, in processing a type block, the type ``name'' gets added last (on the % left) of the queue. The very first reference of its type always follows the % name, since it may form a hyperlink with it (so we keep it stored % separately, in \cs{l_@@_type_first_label_tl}, with % \cs{l_@@_type_first_label_type_tl} being its type). And, since we may need % up to two type blocks in storage before typesetting, we have two of these % ``queues'': \cs{l_@@_typeset_queue_curr_tl} and % \cs{l_@@_typeset_queue_prev_tl}. % % Some of the relevant cases (e.g., distinguishing ``pair'' from ``list'') are % handled by counters, the main ones are: one for the ``type'' % (\cs{l_@@_type_count_int}) and one for the ``label in the current type % block'' (\cs{l_@@_label_count_int}). % % Range compression, in particular, relies heavily on counting to be able do % distinguish relevant cases. \cs{l_@@_range_count_int} counts the number of % elements in the current sequential ``streak'', and % \cs{l_@@_range_same_count_int} counts the number of \emph{equal} elements in % that same ``streak''. The difference between the two allows us to % distinguish the cases in which a range actually ``skips'' a number in the % sequence, in which case we should use a range separator, from when they are % after all just contiguous, in which case a pair separator is called for. % Since, as usual, we can only know this when a arbitrary long ``streak'' % finishes, we have to store the label which (potentially) begins a range % (kept in \cs{l_@@_range_beg_label_tl}). \cs{l_@@_next_maybe_range_bool} % signals when ``next'' is potentially a range with ``current'', and % \cs{l_@@_next_is_same_bool} when their values are actually equal. % % % One further thing to discuss here -- to keep this ``on record'' -- is % inhibition of compression for individual labels. It is not difficult to % handle it at the infrastructure side, what gets sloppy is the user facing % syntax to signal such inhibition. For some possible alternatives for this, % suggested by \contributor{Enrico Gregorio}, \contributor{Phelype Oleinik}, % and \contributor{Steven B.\ Segletes} (and good ones at that) see % \url{https://tex.stackexchange.com/q/611370}. Yet another alternative would % be an option receiving the label(s) not to be compressed, this would be a % repetition, but would keep the syntax clean. All in all, probably the best % is simply not to allow individual inhibition of compression. We can already % control compression of each \cs{zcref} call with existing options, this % should be enough. I don't think the small extra flexibility individual % label control for this would grant is worth the syntax disruption it would % entail. Anyway, it would be easy to deal with this in case the need arose, % by just adding another condition (coming from whatever the chosen syntax % was) when we check for \cs{@@_labels_in_sequence:nn} in % \cs{@@_typeset_refs_not_last_of_type:}. But I remain unconvinced of the % pertinence of doing so. % % % \subsection*{Variables} % % \begin{macro} % { % \l_@@_typeset_labels_seq , % \l_@@_typeset_last_bool , % \l_@@_last_of_type_bool , % } % Auxiliary variables for \cs{@@_typeset_refs:} main stack control. % \begin{macrocode} \seq_new:N \l_@@_typeset_labels_seq \bool_new:N \l_@@_typeset_last_bool \bool_new:N \l_@@_last_of_type_bool % \end{macrocode} % \end{macro} % % \begin{macro} % { % \l_@@_type_count_int , % \l_@@_label_count_int , % } % Auxiliary variables for \cs{@@_typeset_refs:} main counters. % \begin{macrocode} \int_new:N \l_@@_type_count_int \int_new:N \l_@@_label_count_int % \end{macrocode} % \end{macro} % % \begin{macro} % { % \l_@@_label_a_tl , % \l_@@_label_b_tl , % \l_@@_typeset_queue_prev_tl , % \l_@@_typeset_queue_curr_tl , % \l_@@_type_first_label_tl , % \l_@@_type_first_label_type_tl % } % Auxiliary variables for \cs{@@_typeset_refs:} main ``queue'' control and % storage. % \begin{macrocode} \tl_new:N \l_@@_label_a_tl \tl_new:N \l_@@_label_b_tl \tl_new:N \l_@@_typeset_queue_prev_tl \tl_new:N \l_@@_typeset_queue_curr_tl \tl_new:N \l_@@_type_first_label_tl \tl_new:N \l_@@_type_first_label_type_tl % \end{macrocode} % \end{macro} % % \begin{macro} % { % \l_@@_type_name_tl , % \l_@@_name_in_link_bool , % \l_@@_name_format_tl , % \l_@@_name_format_fallback_tl , % \l_@@_type_name_gender_tl , % } % Auxiliary variables for \cs{@@_typeset_refs:} type name handling. % \begin{macrocode} \tl_new:N \l_@@_type_name_tl \bool_new:N \l_@@_name_in_link_bool \tl_new:N \l_@@_name_format_tl \tl_new:N \l_@@_name_format_fallback_tl \tl_new:N \l_@@_type_name_gender_tl % \end{macrocode} % \end{macro} % % \begin{macro} % { % \l_@@_range_count_int , % \l_@@_range_same_count_int , % \l_@@_range_beg_label_tl , % \l_@@_next_maybe_range_bool , % \l_@@_next_is_same_bool , % } % Auxiliary variables for \cs{@@_typeset_refs:} range handling. % \begin{macrocode} \int_new:N \l_@@_range_count_int \int_new:N \l_@@_range_same_count_int \tl_new:N \l_@@_range_beg_label_tl \bool_new:N \l_@@_next_maybe_range_bool \bool_new:N \l_@@_next_is_same_bool % \end{macrocode} % \end{macro} % % \begin{macro} % { % \l_@@_tpairsep_tl , % \l_@@_tlistsep_tl , % \l_@@_tlastsep_tl , % \l_@@_namesep_tl , % \l_@@_pairsep_tl , % \l_@@_listsep_tl , % \l_@@_lastsep_tl , % \l_@@_rangesep_tl , % \l_@@_preref_tl , % \l_@@_postref_tl , % \l_@@_namefont_tl , % \l_@@_reffont_tl , % \l_@@_capitalize_bool , % \l_@@_abbrev_bool , % } % Auxiliary variables for \cs{@@_typeset_refs:} separators, pre-/postref and % font and other options. % \begin{macrocode} \tl_new:N \l_@@_tpairsep_tl \tl_new:N \l_@@_tlistsep_tl \tl_new:N \l_@@_tlastsep_tl \tl_new:N \l_@@_namesep_tl \tl_new:N \l_@@_pairsep_tl \tl_new:N \l_@@_listsep_tl \tl_new:N \l_@@_lastsep_tl \tl_new:N \l_@@_rangesep_tl \tl_new:N \l_@@_preref_tl \tl_new:N \l_@@_postref_tl \tl_new:N \l_@@_namefont_tl \tl_new:N \l_@@_reffont_tl \bool_new:N \l_@@_capitalize_bool \bool_new:N \l_@@_abbrev_bool % \end{macrocode} % \end{macro} % % % \begin{macro}{\l_@@_verbose_testing_bool} % Internal variable which enables extra log messaging at points of interest % in the code for purposes of regression testing. Particularly relevant to % keep track of expansion control in \cs{l_@@_typeset_queue_curr_tl}. % \begin{macrocode} \bool_new:N \l_@@_verbose_testing_bool % \end{macrocode} % \end{macro} % % % \subsection*{Main functions} % % \begin{macro}{\@@_typeset_refs:} % Main typesetting function for \cs{zcref}. % \begin{macrocode} \cs_new_protected:Npn \@@_typeset_refs: { \seq_set_eq:NN \l_@@_typeset_labels_seq \l_@@_zcref_labels_seq \tl_clear:N \l_@@_typeset_queue_prev_tl \tl_clear:N \l_@@_typeset_queue_curr_tl \tl_clear:N \l_@@_type_first_label_tl \tl_clear:N \l_@@_type_first_label_type_tl \tl_clear:N \l_@@_range_beg_label_tl \int_zero:N \l_@@_label_count_int \int_zero:N \l_@@_type_count_int \int_zero:N \l_@@_range_count_int \int_zero:N \l_@@_range_same_count_int % Get type block options (not type-specific). \@@_get_ref_opt_typeset:nN { tpairsep } \l_@@_tpairsep_tl \@@_get_ref_opt_typeset:nN { tlistsep } \l_@@_tlistsep_tl \@@_get_ref_opt_typeset:nN { tlastsep } \l_@@_tlastsep_tl % Process label stack. \bool_set_false:N \l_@@_typeset_last_bool \bool_until_do:Nn \l_@@_typeset_last_bool { \seq_pop_left:NN \l_@@_typeset_labels_seq \l_@@_label_a_tl \seq_if_empty:NTF \l_@@_typeset_labels_seq { \tl_clear:N \l_@@_label_b_tl \bool_set_true:N \l_@@_typeset_last_bool } { \seq_get_left:NN \l_@@_typeset_labels_seq \l_@@_label_b_tl } \tl_if_eq:NnTF \l_@@_ref_property_tl { page } { \tl_set:Nn \l_@@_label_type_a_tl { page } \tl_set:Nn \l_@@_label_type_b_tl { page } } { \@@_extract_default:NVnn \l_@@_label_type_a_tl \l_@@_label_a_tl { zc@type } { zc@missingtype } \@@_extract_default:NVnn \l_@@_label_type_b_tl \l_@@_label_b_tl { zc@type } { zc@missingtype } } % First, we establish whether the "current label" (i.e. `a') is the % last one of its type. This can happen because the "next label" % (i.e. `b') is of a different type (or different definition status), % or because we are at the end of the list. \bool_if:NTF \l_@@_typeset_last_bool { \bool_set_true:N \l_@@_last_of_type_bool } { \zref@ifrefundefined { \l_@@_label_a_tl } { \zref@ifrefundefined { \l_@@_label_b_tl } { \bool_set_false:N \l_@@_last_of_type_bool } { \bool_set_true:N \l_@@_last_of_type_bool } } { \zref@ifrefundefined { \l_@@_label_b_tl } { \bool_set_true:N \l_@@_last_of_type_bool } { % Neither is undefined, we must check the types. \tl_if_eq:NNTF \l_@@_label_type_a_tl \l_@@_label_type_b_tl { \bool_set_false:N \l_@@_last_of_type_bool } { \bool_set_true:N \l_@@_last_of_type_bool } } } } % Handle warnings in case of reference or type undefined. % Test: 'zc-typeset01.lvt': "Typeset refs: warn ref undefined" \zref@refused { \l_@@_label_a_tl } % Test: 'zc-typeset01.lvt': "Typeset refs: warn missing type" \zref@ifrefundefined { \l_@@_label_a_tl } {} { \tl_if_eq:NnT \l_@@_label_type_a_tl { zc@missingtype } { \msg_warning:nnx { zref-clever } { missing-type } { \l_@@_label_a_tl } } \zref@ifrefcontainsprop { \l_@@_label_a_tl } { \l_@@_ref_property_tl } { } { \msg_warning:nnxx { zref-clever } { missing-property } { \l_@@_ref_property_tl } { \l_@@_label_a_tl } } } % Get type-specific separators, pre-/postref font and other options, % once per type. \int_compare:nNnT { \l_@@_label_count_int } = { 0 } { \@@_get_ref_opt_typeset:nN { namesep } \l_@@_namesep_tl \@@_get_ref_opt_typeset:nN { pairsep } \l_@@_pairsep_tl \@@_get_ref_opt_typeset:nN { listsep } \l_@@_listsep_tl \@@_get_ref_opt_typeset:nN { lastsep } \l_@@_lastsep_tl \@@_get_ref_opt_typeset:nN { rangesep } \l_@@_rangesep_tl \@@_get_ref_opt_typeset:nN { preref } \l_@@_preref_tl \@@_get_ref_opt_typeset:nN { postref } \l_@@_postref_tl \@@_get_ref_opt_font:nN { namefont } \l_@@_namefont_tl \@@_get_ref_opt_font:nN { reffont } \l_@@_reffont_tl \@@_get_ref_opt_bool:nnN { cap } { false } \l_@@_capitalize_bool \@@_get_ref_opt_bool:nnN { abbrev } { false } \l_@@_abbrev_bool } % Here we send this to a couple of auxiliary functions. \bool_if:NTF \l_@@_last_of_type_bool % There exists no next label of the same type as the current. { \@@_typeset_refs_last_of_type: } % There exists a next label of the same type as the current. { \@@_typeset_refs_not_last_of_type: } } } % \end{macrocode} % \end{macro} % % % This is actually the one meaningful ``big branching'' we can do while % processing the label stack: i) the ``current'' label is the last of its type % block; or ii) the ``current'' label is \emph{not} the last of its type % block. Indeed, as mentioned above, quite a number of things can only be % decided when the type block ends, and we only know this when we look at the % ``next'' label and find something of a different ``type'' (loose here, maybe % different definition status, maybe end of stack). So, though this is not % very strict, \cs{@@_typeset_refs_last_of_type:} is more of a ``wrapping % up'' function, and it is indeed the one which does the actual typesetting, % while \cs{@@_typeset_refs_not_last_of_type:} is more of an % ``accumulation'' function. % % % \begin{macro}{\@@_typeset_refs_last_of_type:} % Handles typesetting when the current label is the last of its type. % \begin{macrocode} \cs_new_protected:Npn \@@_typeset_refs_last_of_type: { % Process the current label to the current queue. \int_case:nnF { \l_@@_label_count_int } { % It is the last label of its type, but also the first one, and that's % what matters here: just store it. % Test: 'zc-typeset01.lvt': "Last of type: single" { 0 } { \tl_set:NV \l_@@_type_first_label_tl \l_@@_label_a_tl \tl_set:NV \l_@@_type_first_label_type_tl \l_@@_label_type_a_tl } % The last is the second: we have a pair (if not repeated). % Test: 'zc-typeset01.lvt': "Last of type: pair" { 1 } { \int_compare:nNnF { \l_@@_range_same_count_int } = { 1 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \exp_not:V \l_@@_pairsep_tl \@@_get_ref:V \l_@@_label_a_tl } } } } % Last is third or more of its type: without repetition, we'd have the % last element on a list, but control for possible repetition. { \int_case:nnF { \l_@@_range_count_int } { % There was no range going on. % Test: 'zc-typeset01.lvt': "Last of type: not range" { 0 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \exp_not:V \l_@@_lastsep_tl \@@_get_ref:V \l_@@_label_a_tl } } % Last in the range is also the second in it. % Test: 'zc-typeset01.lvt': "Last of type: pair in sequence" { 1 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { % We know `range_beg_label' is not empty, since this is the % second element in the range, but the third or more in the % type list. \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl \int_compare:nNnF { \l_@@_range_same_count_int } = { 1 } { \exp_not:V \l_@@_lastsep_tl \@@_get_ref:V \l_@@_label_a_tl } } } } % Last in the range is third or more in it. { \int_case:nnF { \l_@@_range_count_int - \l_@@_range_same_count_int } { % Repetition, not a range. % Test: 'zc-typeset01.lvt': "Last of type: range to one" { 0 } { % If `range_beg_label' is empty, it means it was also the % first of the type, and hence was already handled. \tl_if_empty:VF \l_@@_range_beg_label_tl { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \exp_not:V \l_@@_lastsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } } } % A `range', but with no skipped value, treat as list. % Test: 'zc-typeset01.lvt': "Last of type: range to pair" { 1 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { % Ditto. \tl_if_empty:VF \l_@@_range_beg_label_tl { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } \exp_not:V \l_@@_lastsep_tl \@@_get_ref:V \l_@@_label_a_tl } } } { % An actual range. % Test: 'zc-typeset01.lvt': "Last of type: range" \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { % Ditto. \tl_if_empty:VF \l_@@_range_beg_label_tl { \exp_not:V \l_@@_lastsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } \exp_not:V \l_@@_rangesep_tl \@@_get_ref:V \l_@@_label_a_tl } } } } % Handle "range" option. The idea is simple: if the queue is not empty, % we replace it with the end of the range (or pair). We can still % retrieve the end of the range from `label_a' since we know to be % processing the last label of its type at this point. \bool_if:NT \l_@@_typeset_range_bool { \tl_if_empty:NTF \l_@@_typeset_queue_curr_tl { \zref@ifrefundefined { \l_@@_type_first_label_tl } { } { \msg_warning:nnx { zref-clever } { single-element-range } { \l_@@_type_first_label_type_tl } } } { \bool_set_false:N \l_@@_next_maybe_range_bool \zref@ifrefundefined { \l_@@_type_first_label_tl } { } { \@@_labels_in_sequence:nn { \l_@@_type_first_label_tl } { \l_@@_label_a_tl } } % Test: 'zc-typeset01.lvt': "Last of type: option range" % Test: 'zc-typeset01.lvt': "Last of type: option range to pair" \tl_set:Nx \l_@@_typeset_queue_curr_tl { \bool_if:NTF \l_@@_next_maybe_range_bool { \exp_not:V \l_@@_pairsep_tl } { \exp_not:V \l_@@_rangesep_tl } \@@_get_ref:V \l_@@_label_a_tl } } } % Now that the type block is finished, we can add the name and the first % ref to the queue. Also, if "typeset" option is not "both", handle it % here as well. \@@_type_name_setup: \bool_if:nTF { \l_@@_typeset_ref_bool && \l_@@_typeset_name_bool } { \tl_put_left:Nx \l_@@_typeset_queue_curr_tl { \@@_get_ref_first: } } { \bool_if:NTF \l_@@_typeset_ref_bool { % Test: 'zc-typeset01.lvt': "Last of type: option typeset ref" \tl_put_left:Nx \l_@@_typeset_queue_curr_tl { \@@_get_ref:V \l_@@_type_first_label_tl } } { \bool_if:NTF \l_@@_typeset_name_bool { % Test: 'zc-typeset01.lvt': "Last of type: option typeset name" \tl_set:Nx \l_@@_typeset_queue_curr_tl { \bool_if:NTF \l_@@_name_in_link_bool { \exp_not:N \group_begin: \exp_not:V \l_@@_namefont_tl % It's two '@s', but escaped for DocStrip. \exp_not:N \hyper@@@@link { \@@_extract_url_unexp:V \l_@@_type_first_label_tl } { \@@_extract_unexp:Vnn \l_@@_type_first_label_tl { anchor } { } } { \exp_not:V \l_@@_type_name_tl } \exp_not:N \group_end: } { \exp_not:N \group_begin: \exp_not:V \l_@@_namefont_tl \exp_not:V \l_@@_type_name_tl \exp_not:N \group_end: } } } { % Logically, this case would correspond to "typeset=none", but % it should not occur, given that the options are set up to % typeset either "ref" or "name". Still, leave here a % sensible fallback, equal to the behavior of "both". % Test: 'zc-typeset01.lvt': "Last of type: option typeset none" \tl_put_left:Nx \l_@@_typeset_queue_curr_tl { \@@_get_ref_first: } } } } % Typeset the previous type block, if there is one. \int_compare:nNnT { \l_@@_type_count_int } > { 0 } { \int_compare:nNnT { \l_@@_type_count_int } > { 1 } { \l_@@_tlistsep_tl } \l_@@_typeset_queue_prev_tl } % Extra log for testing. \bool_if:NT \l_@@_verbose_testing_bool { \tl_show:N \l_@@_typeset_queue_curr_tl } % Wrap up loop, or prepare for next iteration. \bool_if:NTF \l_@@_typeset_last_bool { % We are finishing, typeset the current queue. \int_case:nnF { \l_@@_type_count_int } { % Single type. % Test: 'zc-typeset01.lvt': "Last of type: single type" { 0 } { \l_@@_typeset_queue_curr_tl } % Pair of types. % Test: 'zc-typeset01.lvt': "Last of type: pair of types" { 1 } { \l_@@_tpairsep_tl \l_@@_typeset_queue_curr_tl } } { % Last in list of types. % Test: 'zc-typeset01.lvt': "Last of type: list of types" \l_@@_tlastsep_tl \l_@@_typeset_queue_curr_tl } % And nudge in case of multitype reference. \bool_lazy_all:nT { { \l_@@_nudge_enabled_bool } { \l_@@_nudge_multitype_bool } { \int_compare_p:nNn { \l_@@_type_count_int } > { 0 } } } { \msg_warning:nn { zref-clever } { nudge-multitype } } } { % There are further labels, set variables for next iteration. \tl_set_eq:NN \l_@@_typeset_queue_prev_tl \l_@@_typeset_queue_curr_tl \tl_clear:N \l_@@_typeset_queue_curr_tl \tl_clear:N \l_@@_type_first_label_tl \tl_clear:N \l_@@_type_first_label_type_tl \tl_clear:N \l_@@_range_beg_label_tl \int_zero:N \l_@@_label_count_int \int_incr:N \l_@@_type_count_int \int_zero:N \l_@@_range_count_int \int_zero:N \l_@@_range_same_count_int } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_typeset_refs_not_last_of_type:} % Handles typesetting when the current label is not the last of its type. % \begin{macrocode} \cs_new_protected:Npn \@@_typeset_refs_not_last_of_type: { % Signal if next label may form a range with the current one (only % considered if compression is enabled in the first place). \bool_set_false:N \l_@@_next_maybe_range_bool \bool_set_false:N \l_@@_next_is_same_bool \bool_if:NT \l_@@_typeset_compress_bool { \zref@ifrefundefined { \l_@@_label_a_tl } { } { \@@_labels_in_sequence:nn { \l_@@_label_a_tl } { \l_@@_label_b_tl } } } % Process the current label to the current queue. \int_compare:nNnTF { \l_@@_label_count_int } = { 0 } { % Current label is the first of its type (also not the last, but it % doesn't matter here): just store the label. \tl_set:NV \l_@@_type_first_label_tl \l_@@_label_a_tl \tl_set:NV \l_@@_type_first_label_type_tl \l_@@_label_type_a_tl % If the next label may be part of a range, we set `range_beg_label' % to "empty" (we deal with it as the "first", and must do it there, to % handle hyperlinking), but also step the range counters. % Test: 'zc-typeset01.lvt': "Not last of type: first is range" \bool_if:NT \l_@@_next_maybe_range_bool { \tl_clear:N \l_@@_range_beg_label_tl \int_incr:N \l_@@_range_count_int \bool_if:NT \l_@@_next_is_same_bool { \int_incr:N \l_@@_range_same_count_int } } } { % Current label is neither the first (nor the last) of its type. \bool_if:NTF \l_@@_next_maybe_range_bool { % Starting, or continuing a range. \int_compare:nNnTF { \l_@@_range_count_int } = { 0 } { % There was no range going, we are starting one. \tl_set:NV \l_@@_range_beg_label_tl \l_@@_label_a_tl \int_incr:N \l_@@_range_count_int \bool_if:NT \l_@@_next_is_same_bool { \int_incr:N \l_@@_range_same_count_int } } { % Second or more in the range, but not the last. \int_incr:N \l_@@_range_count_int \bool_if:NT \l_@@_next_is_same_bool { \int_incr:N \l_@@_range_same_count_int } } } { % Next element is not in sequence: there was no range, or we are % closing one. \int_case:nnF { \l_@@_range_count_int } { % There was no range going on. % Test: 'zc-typeset01.lvt': "Not last of type: no range" { 0 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_label_a_tl } } % Last is second in the range: if `range_same_count' is also % `1', it's a repetition (drop it), otherwise, it's a "pair % within a list", treat as list. % Test: 'zc-typeset01.lvt': "Not last of type: range pair to one" % Test: 'zc-typeset01.lvt': "Not last of type: range pair" { 1 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \tl_if_empty:VF \l_@@_range_beg_label_tl { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } \int_compare:nNnF { \l_@@_range_same_count_int } = { 1 } { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_label_a_tl } } } } { % Last is third or more in the range: if `range_count' and % `range_same_count' are the same, its a repetition (drop it), % if they differ by `1', its a list, if they differ by more, % it is a real range. \int_case:nnF { \l_@@_range_count_int - \l_@@_range_same_count_int } { % Test: 'zc-typeset01.lvt': "Not last of type: range to one" { 0 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \tl_if_empty:VF \l_@@_range_beg_label_tl { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } } } % Test: 'zc-typeset01.lvt': "Not last of type: range to pair" { 1 } { \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \tl_if_empty:VF \l_@@_range_beg_label_tl { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_label_a_tl } } } { % Test: 'zc-typeset01.lvt': "Not last of type: range" \tl_put_right:Nx \l_@@_typeset_queue_curr_tl { \tl_if_empty:VF \l_@@_range_beg_label_tl { \exp_not:V \l_@@_listsep_tl \@@_get_ref:V \l_@@_range_beg_label_tl } \exp_not:V \l_@@_rangesep_tl \@@_get_ref:V \l_@@_label_a_tl } } } % Reset counters. \int_zero:N \l_@@_range_count_int \int_zero:N \l_@@_range_same_count_int } } % Step label counter for next iteration. \int_incr:N \l_@@_label_count_int } % \end{macrocode} % \end{macro} % % % % \subsection*{Auxiliary functions} % % \cs{@@_get_ref:n} and \cs{@@_get_ref_first:} are the two functions which % actually build the reference blocks for typesetting. \cs{@@_get_ref:n} % handles all references but the first of its type, and \cs{@@_get_ref_first:} % deals with the first reference of a type. Saying they do ``typesetting'' is % imprecise though, they actually prepare material to be accumulated in % \cs{l_@@_typeset_queue_curr_tl} inside \cs{@@_typeset_refs_last_of_type:} % and \cs{@@_typeset_refs_not_last_of_type:}. And this difference results % quite crucial for the \TeX{}nical requirements of these functions. This % because, as we are processing the label stack and accumulating content in % the queue, we are using a number of variables which are transient to the % current label, the label properties among them, but not only. Hence, these % variables \emph{must} be expanded to their current values to be stored in % the queue. Indeed, \cs{@@_get_ref:n} and \cs{@@_get_ref_first:} get called, % as they must, in the context of \texttt{x} type expansions. But we don't % want to expand the values of the variables themselves, so we need to get % current values, but stop expansion after that. In particular, reference % options given by the user should reach the stream for its final typesetting % (when the queue itself gets typeset) \emph{unmodified} (``no manipulation'', % to use the \texttt{n} signature jargon). We also need to prevent premature % expansion of material that can't be expanded at this point (e.g. grouping, % \cs{zref@default} or \cs[replace=false]{hyper@@link}). In a nutshell, the % job of these two functions is putting the pieces in place, but with proper % expansion control. % % % \begin{macro}{\@@_ref_default:, \@@_name_default:} % Default values for undefined references and undefined type names, % respectively. We are ultimately using \cs{zref@default}, but calls to it % should be made through these internal functions, according to the case. % As a bonus, we don't need to protect them with \cs{exp_not:N}, as % \cs{zref@default} would require, since we already define them protected. % \begin{macrocode} \cs_new_protected:Npn \@@_ref_default: { \zref@default } \cs_new_protected:Npn \@@_name_default: { \zref@default } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_get_ref:n} % Handles a complete reference block to be accumulated in the ``queue'', % including ``pre'' and ``pos'' elements, and hyperlinking. For use with % all labels, except the first of its type, which is done by % \cs{@@_get_ref_first:}. % \begin{syntax} % \cs{@@_get_ref:n} \Arg{label} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_get_ref:n #1 { \zref@ifrefcontainsprop {#1} { \l_@@_ref_property_tl } { \bool_if:nTF { \l_@@_use_hyperref_bool && ! \l_@@_link_star_bool } { \bool_if:NF \l_@@_preposinlink_bool { \exp_not:V \l_@@_preref_tl } % It's two `@s', but escaped for DocStrip. \exp_not:N \hyper@@@@link { \@@_extract_url_unexp:n {#1} } { \@@_extract_unexp:nnn {#1} { anchor } { } } { \bool_if:NT \l_@@_preposinlink_bool { \exp_not:V \l_@@_preref_tl } \exp_not:N \group_begin: \exp_not:V \l_@@_reffont_tl \@@_extract_unexp:nvn {#1} { l_@@_ref_property_tl } { } \exp_not:N \group_end: \bool_if:NT \l_@@_preposinlink_bool { \exp_not:V \l_@@_postref_tl } } \bool_if:NF \l_@@_preposinlink_bool { \exp_not:V \l_@@_postref_tl } } { \exp_not:V \l_@@_preref_tl \exp_not:N \group_begin: \exp_not:V \l_@@_reffont_tl \@@_extract_unexp:nvn {#1} { l_@@_ref_property_tl } { } \exp_not:N \group_end: \exp_not:V \l_@@_postref_tl } } { \@@_ref_default: } } \cs_generate_variant:Nn \@@_get_ref:n { V } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_ref_first:} % Handles a complete reference block for the first label of its type to be % accumulated in the ``queue'', including ``pre'' and ``pos'' elements, % hyperlinking, and the reference type ``name''. It does not receive % arguments, but relies on being called in the appropriate place in % \cs{@@_typeset_refs_last_of_type:} where a number of variables are % expected to be appropriately set for it to consume. Prominently among % those is \cs{l_@@_type_first_label_tl}, but it also expected to be called % right after \cs{@@_type_name_setup:} which sets \cs{l_@@_type_name_tl} and % \cs{l_@@_name_in_link_bool} which it uses. % \begin{macrocode} \cs_new:Npn \@@_get_ref_first: { \zref@ifrefundefined { \l_@@_type_first_label_tl } { \@@_ref_default: } { \bool_if:NTF \l_@@_name_in_link_bool { \zref@ifrefcontainsprop { \l_@@_type_first_label_tl } { \l_@@_ref_property_tl } { % It's two `@s', but escaped for DocStrip. \exp_not:N \hyper@@@@link { \@@_extract_url_unexp:V \l_@@_type_first_label_tl } { \@@_extract_unexp:Vnn \l_@@_type_first_label_tl { anchor } { } } { \exp_not:N \group_begin: \exp_not:V \l_@@_namefont_tl \exp_not:V \l_@@_type_name_tl \exp_not:N \group_end: \exp_not:V \l_@@_namesep_tl \exp_not:V \l_@@_preref_tl \exp_not:N \group_begin: \exp_not:V \l_@@_reffont_tl \@@_extract_unexp:Vvn \l_@@_type_first_label_tl { l_@@_ref_property_tl } { } \exp_not:N \group_end: \bool_if:NT \l_@@_preposinlink_bool { \exp_not:V \l_@@_postref_tl } } \bool_if:NF \l_@@_preposinlink_bool { \exp_not:V \l_@@_postref_tl } } { \exp_not:N \group_begin: \exp_not:V \l_@@_namefont_tl \exp_not:V \l_@@_type_name_tl \exp_not:N \group_end: \exp_not:V \l_@@_namesep_tl \@@_ref_default: } } { \tl_if_empty:NTF \l_@@_type_name_tl { \@@_name_default: \exp_not:V \l_@@_namesep_tl } { \exp_not:N \group_begin: \exp_not:V \l_@@_namefont_tl \exp_not:V \l_@@_type_name_tl \exp_not:N \group_end: \exp_not:V \l_@@_namesep_tl } \zref@ifrefcontainsprop { \l_@@_type_first_label_tl } { \l_@@_ref_property_tl } { \bool_if:nTF { \l_@@_use_hyperref_bool && ! \l_@@_link_star_bool } { \bool_if:NF \l_@@_preposinlink_bool { \exp_not:V \l_@@_preref_tl } % It's two '@s', but escaped for DocStrip. \exp_not:N \hyper@@@@link { \@@_extract_url_unexp:V \l_@@_type_first_label_tl } { \@@_extract_unexp:Vnn \l_@@_type_first_label_tl { anchor } { } } { \bool_if:NT \l_@@_preposinlink_bool { \exp_not:V \l_@@_preref_tl } \exp_not:N \group_begin: \exp_not:V \l_@@_reffont_tl \@@_extract_unexp:Vvn \l_@@_type_first_label_tl { l_@@_ref_property_tl } { } \exp_not:N \group_end: \bool_if:NT \l_@@_preposinlink_bool { \exp_not:V \l_@@_postref_tl } } \bool_if:NF \l_@@_preposinlink_bool { \exp_not:V \l_@@_postref_tl } } { \exp_not:V \l_@@_preref_tl \exp_not:N \group_begin: \exp_not:V \l_@@_reffont_tl \@@_extract_unexp:Vvn \l_@@_type_first_label_tl { l_@@_ref_property_tl } { } \exp_not:N \group_end: \exp_not:V \l_@@_postref_tl } } { \@@_ref_default: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_type_name_setup:} % Auxiliary function to \cs{@@_typeset_refs_last_of_type:}. It is % responsible for setting the type name variable \cs{l_@@_type_name_tl} and % \cs{l_@@_name_in_link_bool}. If a type name can't be found, % \cs{l_@@_type_name_tl} is cleared. The function takes no arguments, but % is expected to be called in \cs{@@_typeset_refs_last_of_type:} right % before \cs{@@_get_ref_first:}, which is the main consumer of the variables % it sets, though not the only one (and hence this cannot be moved into % \cs{@@_get_ref_first:} itself). It also expects a number of relevant % variables to have been appropriately set, and which it uses, prominently % \cs{l_@@_type_first_label_type_tl}, but also the queue itself in % \cs{l_@@_typeset_queue_curr_tl}, which should be ``ready except for the % first label'', and the type counter \cs{l_@@_type_count_int}. % \begin{macrocode} \cs_new_protected:Npn \@@_type_name_setup: { \zref@ifrefundefined { \l_@@_type_first_label_tl } { \tl_clear:N \l_@@_type_name_tl } { \tl_if_eq:NnTF \l_@@_type_first_label_type_tl { zc@missingtype } { \tl_clear:N \l_@@_type_name_tl } { % Determine whether we should use capitalization, abbreviation, % and plural. \bool_lazy_or:nnTF { \l_@@_capitalize_bool } { \l_@@_capitalize_first_bool && \int_compare_p:nNn { \l_@@_type_count_int } = { 0 } } { \tl_set:Nn \l_@@_name_format_tl {Name} } { \tl_set:Nn \l_@@_name_format_tl {name} } % If the queue is empty, we have a singular, otherwise, plural. \tl_if_empty:NTF \l_@@_typeset_queue_curr_tl { \tl_put_right:Nn \l_@@_name_format_tl { -sg } } { \tl_put_right:Nn \l_@@_name_format_tl { -pl } } \bool_lazy_and:nnTF { \l_@@_abbrev_bool } { ! \int_compare_p:nNn { \l_@@_type_count_int } = { 0 } || ! \l_@@_noabbrev_first_bool } { \tl_set:NV \l_@@_name_format_fallback_tl \l_@@_name_format_tl \tl_put_right:Nn \l_@@_name_format_tl { -ab } } { \tl_clear:N \l_@@_name_format_fallback_tl } % Handle number and gender nudges. \bool_if:NT \l_@@_nudge_enabled_bool { \bool_if:NTF \l_@@_nudge_singular_bool { \tl_if_empty:NF \l_@@_typeset_queue_curr_tl { \msg_warning:nnx { zref-clever } { nudge-plural-when-sg } { \l_@@_type_first_label_type_tl } } } { \bool_lazy_all:nT { { \l_@@_nudge_comptosing_bool } { \tl_if_empty_p:N \l_@@_typeset_queue_curr_tl } { \int_compare_p:nNn { \l_@@_label_count_int } > { 0 } } } { \msg_warning:nnx { zref-clever } { nudge-comptosing } { \l_@@_type_first_label_type_tl } } } \bool_lazy_and:nnT { \l_@@_nudge_gender_bool } { ! \tl_if_empty_p:N \l_@@_ref_gender_tl } { \@@_get_lang_opt_type:xxnNF { \l_@@_ref_language_tl } { \l_@@_type_first_label_type_tl } { gender } \l_@@_type_name_gender_tl { \tl_clear:N \l_@@_type_name_gender_tl } \clist_set:NV \l_tmpa_clist \l_@@_type_name_gender_tl \clist_if_in:NVF \l_tmpa_clist \l_@@_ref_gender_tl { \tl_if_empty:NTF \l_@@_type_name_gender_tl { \msg_warning:nnxxx { zref-clever } { nudge-gender-not-declared-for-type } { \l_@@_ref_gender_tl } { \l_@@_type_first_label_type_tl } { \l_@@_ref_language_tl } } { \msg_warning:nnxxxx { zref-clever } { nudge-gender-mismatch } { \l_@@_type_first_label_type_tl } { \l_@@_ref_gender_tl } { \l_@@_type_name_gender_tl } { \l_@@_ref_language_tl } } } } } \tl_if_empty:NTF \l_@@_name_format_fallback_tl { \prop_get:cVNF { l_@@_type_ \l_@@_type_first_label_type_tl _options_prop } \l_@@_name_format_tl \l_@@_type_name_tl { \tl_if_empty:NF \l_@@_ref_decl_case_tl { \tl_put_left:Nn \l_@@_name_format_tl { - } \tl_put_left:NV \l_@@_name_format_tl \l_@@_ref_decl_case_tl } \@@_get_lang_opt_type:xxxNF { \l_@@_ref_language_tl } { \l_@@_type_first_label_type_tl } { \l_@@_name_format_tl } \l_@@_type_name_tl { \tl_clear:N \l_@@_type_name_tl \msg_warning:nnxx { zref-clever } { missing-name } { \l_@@_name_format_tl } { \l_@@_type_first_label_type_tl } } } } { \prop_get:cVNF { l_@@_type_ \l_@@_type_first_label_type_tl _options_prop } \l_@@_name_format_tl \l_@@_type_name_tl { \prop_get:cVNF { l_@@_type_ \l_@@_type_first_label_type_tl _options_prop } \l_@@_name_format_fallback_tl \l_@@_type_name_tl { \tl_if_empty:NF \l_@@_ref_decl_case_tl { \tl_put_left:Nn \l_@@_name_format_tl { - } \tl_put_left:NV \l_@@_name_format_tl \l_@@_ref_decl_case_tl \tl_put_left:Nn \l_@@_name_format_fallback_tl { - } \tl_put_left:NV \l_@@_name_format_fallback_tl \l_@@_ref_decl_case_tl } \@@_get_lang_opt_type:xxxNF { \l_@@_ref_language_tl } { \l_@@_type_first_label_type_tl } { \l_@@_name_format_tl } \l_@@_type_name_tl { \@@_get_lang_opt_type:xxxNF { \l_@@_ref_language_tl } { \l_@@_type_first_label_type_tl } { \l_@@_name_format_fallback_tl } \l_@@_type_name_tl { \tl_clear:N \l_@@_type_name_tl \msg_warning:nnxx { zref-clever } { missing-name } { \l_@@_name_format_tl } { \l_@@_type_first_label_type_tl } } } } } } } } % Signal whether the type name is to be included in the hyperlink or not. \bool_lazy_any:nTF { { ! \l_@@_use_hyperref_bool } { \l_@@_link_star_bool } { \tl_if_empty_p:N \l_@@_type_name_tl } { \str_if_eq_p:Vn \l_@@_nameinlink_str { false } } } { \bool_set_false:N \l_@@_name_in_link_bool } { \bool_lazy_any:nTF { { \str_if_eq_p:Vn \l_@@_nameinlink_str { true } } { \str_if_eq_p:Vn \l_@@_nameinlink_str { tsingle } && \tl_if_empty_p:N \l_@@_typeset_queue_curr_tl } { \str_if_eq_p:Vn \l_@@_nameinlink_str { single } && \tl_if_empty_p:N \l_@@_typeset_queue_curr_tl && \l_@@_typeset_last_bool && \int_compare_p:nNn { \l_@@_type_count_int } = { 0 } } } { \bool_set_true:N \l_@@_name_in_link_bool } { \bool_set_false:N \l_@@_name_in_link_bool } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_extract_url_unexp:n} % A convenience auxiliary function for extraction of the \texttt{url} / % \texttt{urluse} property, provided by the \pkg{zref-xr} module. Ensure % that, in the context of an x expansion, \cs{zref@extractdefault} is % expanded exactly twice, but no further to retrieve the proper value. See % documentation for \cs{@@_extract_unexp:nnn}. % \begin{macrocode} \cs_new:Npn \@@_extract_url_unexp:n #1 { \zref@ifpropundefined { urluse } { \@@_extract_unexp:nnn {#1} { url } { \c_empty_tl } } { \zref@ifrefcontainsprop {#1} { urluse } { \@@_extract_unexp:nnn {#1} { urluse } { \c_empty_tl } } { \@@_extract_unexp:nnn {#1} { url } { \c_empty_tl } } } } \cs_generate_variant:Nn \@@_extract_url_unexp:n { V } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_labels_in_sequence:nn} % Auxiliary function to \cs{@@_typeset_refs_not_last_of_type:}. Sets % \cs{l_@@_next_maybe_range_bool} to true if \meta{label b} comes in % immediate sequence from \meta{label a}. And sets both % \cs{l_@@_next_maybe_range_bool} and \cs{l_@@_next_is_same_bool} to true if % the two labels are the ``same'' (that is, have the same counter value). % These two boolean variables are the basis for all range and compression % handling inside \cs{@@_typeset_refs_not_last_of_type:}, so this function % is expected to be called at its beginning, if compression is enabled. % \begin{syntax} % \cs{@@_labels_in_sequence:nn} \Arg{label a} \Arg{label b} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_labels_in_sequence:nn #1#2 { \@@_extract_default:Nnnn \l_@@_label_extdoc_a_tl {#1} { externaldocument } { \c_empty_tl } \@@_extract_default:Nnnn \l_@@_label_extdoc_b_tl {#2} { externaldocument } { \c_empty_tl } \tl_if_eq:NNT \l_@@_label_extdoc_a_tl \l_@@_label_extdoc_b_tl { \tl_if_eq:NnTF \l_@@_ref_property_tl { page } { \exp_args:Nxx \tl_if_eq:nnT { \@@_extract_unexp:nnn {#1} { zc@pgfmt } { } } { \@@_extract_unexp:nnn {#2} { zc@pgfmt } { } } { \int_compare:nNnTF { \@@_extract:nnn {#1} { zc@pgval } { -2 } + 1 } = { \@@_extract:nnn {#2} { zc@pgval } { -1 } } { \bool_set_true:N \l_@@_next_maybe_range_bool } { \int_compare:nNnT { \@@_extract:nnn {#1} { zc@pgval } { -1 } } = { \@@_extract:nnn {#2} { zc@pgval } { -1 } } { \bool_set_true:N \l_@@_next_maybe_range_bool \bool_set_true:N \l_@@_next_is_same_bool } } } } { \exp_args:Nxx \tl_if_eq:nnT { \@@_extract_unexp:nnn {#1} { zc@counter } { } } { \@@_extract_unexp:nnn {#2} { zc@counter } { } } { \exp_args:Nxx \tl_if_eq:nnT { \@@_extract_unexp:nnn {#1} { zc@enclval } { } } { \@@_extract_unexp:nnn {#2} { zc@enclval } { } } { \int_compare:nNnTF { \@@_extract:nnn {#1} { zc@cntval } { -2 } + 1 } = { \@@_extract:nnn {#2} { zc@cntval } { -1 } } { \bool_set_true:N \l_@@_next_maybe_range_bool } { \int_compare:nNnT { \@@_extract:nnn {#1} { zc@cntval } { -1 } } = { \@@_extract:nnn {#2} { zc@cntval } { -1 } } { \bool_set_true:N \l_@@_next_maybe_range_bool \exp_args:Nxx \tl_if_eq:nnT { \@@_extract_unexp:nvn {#1} { l_@@_ref_property_tl } { } } { \@@_extract_unexp:nvn {#2} { l_@@_ref_property_tl } { } } { \bool_set_true:N \l_@@_next_is_same_bool } } } } } } } } % \end{macrocode} % \end{macro} % % % % Finally, some functions for retrieving reference options values, according % to the relevant precedence rules. They both receive an \meta{option} as % argument, and store the retrieved value in an appropriate. Though these are % mostly general functions (for a change\dots{}), they are not completely so, % they rely on the current state of \cs{l_@@_label_type_a_tl}, as set during % the processing of the label stack. This could be easily generalized, of % course, but I don't think it is worth it, \cs{l_@@_label_type_a_tl} is % indeed what we want in all practical cases. The difference between each of % these functions is the kind of option each should be used for. % \cs{@@_get_ref_opt_typeset:nN} is meant for the general options, and % attempts to find values for them in all precedence levels (four plus % ``fallback''). \cs{@@_get_ref_opt_font:nN} is intended for ``font'' % options, which cannot be ``language-specific'', thus for these we just % search general options and type options. \cs{@@_get_ref_opt_bool:nN} is % intended for boolean options. % % \begin{macro}{\@@_get_ref_opt_typeset:nN} % \begin{syntax} % \cs{@@_get_ref_opt_typeset:nN} \Arg{option} \Arg{tl variable} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_get_ref_opt_typeset:nN #1#2 { % First attempt: general options. \prop_get:NnNF \l_@@_ref_options_prop {#1} #2 { % If not found, try type specific options. \bool_lazy_and:nnTF { \prop_if_exist_p:c { l_@@_type_ \l_@@_label_type_a_tl _options_prop } } { \prop_if_in_p:cn { l_@@_type_ \l_@@_label_type_a_tl _options_prop } {#1} } { \prop_get:cnN { l_@@_type_ \l_@@_label_type_a_tl _options_prop } {#1} #2 } { % If not found, try type- and language-specific. \@@_get_lang_opt_type:xxnNF { \l_@@_ref_language_tl } { \l_@@_label_type_a_tl } {#1} #2 { % If not found, try language-specific default. \@@_get_lang_opt_default:xnNF { \l_@@_ref_language_tl } {#1} #2 { % If not found, try fallback. \@@_get_fallback_unknown_lang_opt:nNF {#1} #2 { \tl_clear:N #2 \msg_warning:nnn { zref-clever } { missing-string } {#1} } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_ref_opt_font:nN} % \begin{syntax} % \cs{@@_get_ref_opt_font:nN} \Arg{option} \Arg{tl variable} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_get_ref_opt_font:nN #1#2 { % First attempt: general options. \prop_get:NnNF \l_@@_ref_options_prop {#1} #2 { % If not found, try type specific options. \bool_if:nTF { \prop_if_exist_p:c { l_@@_type_ \l_@@_label_type_a_tl _options_prop } } { \prop_get:cnNF { l_@@_type_ \l_@@_label_type_a_tl _options_prop } {#1} #2 { \tl_clear:N #2 } } { \tl_clear:N #2 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_get_ref_opt_bool:nnN} % \begin{syntax} % \cs{@@_get_ref_opt_bool:nN} \Arg{option} % ~~\Arg{default} \Arg{bool variable} % \end{syntax} % \begin{macrocode} \cs_new_protected:Npn \@@_get_ref_opt_bool:nnN #1#2#3 { % First attempt: general options. \prop_get:NnNF \l_@@_ref_options_prop {#1} \l_tmpa_tl { % If not found, try type specific options. \bool_lazy_and:nnTF { \prop_if_exist_p:c { l_@@_type_ \l_@@_label_type_a_tl _options_prop } } { \prop_if_in_p:cn { l_@@_type_ \l_@@_label_type_a_tl _options_prop } {#1} } { \prop_get:cnN { l_@@_type_ \l_@@_label_type_a_tl _options_prop } {#1} \l_tmpa_tl } { % If not found, try type- and language-specific. \@@_get_lang_opt_type:xxnNF { \l_@@_ref_language_tl } { \l_@@_label_type_a_tl } {#1} \l_tmpa_tl { % If not found, try language-specific default. \@@_get_lang_opt_default:xnNF { \l_@@_ref_language_tl } {#1} \l_tmpa_tl { % If not found, use default argument. \bool_lazy_or:nnTF { \str_if_eq_p:nn {#2} { true } } { \str_if_eq_p:nn {#2} { false } } { \tl_set:Nn \l_tmpa_tl {#2} } { % And, if even that fails, presume false. \tl_set:Nn \l_tmpa_tl { false } } } } } } % Having retrieved the option value, set the boolean. At this point, we % *know* '\l_tmpa_tl' is either 'true' or 'false'. \use:c { bool_set_ \l_tmpa_tl :N } #3 } % \end{macrocode} % \end{macro} % % % \section{Compatibility} % % This section is meant to aggregate any ``special handling'' needed for % \LaTeX{} kernel features, document classes, and packages, needed for % \pkg{zref-clever} to work properly with them. % % % \subsection{\opt{appendix}} % % One relevant case of different reference types sharing the same counter is % the \cs{appendix} which in some document classes, including the standard % ones, change the sectioning commands looks but, of course, keep using the % same counter. \file{book.cls} and \file{report.cls} reset counters % \texttt{chapter} and \texttt{section} to 0, change \cs{@chapapp} to use % \cs{appendixname} and use \cs{@Alph} for \cs{thechapter}. \file{article.cls} % resets counters \texttt{section} and \texttt{subsection} to 0, and uses % \cs{@Alph} for \cs{thesection}. \file{memoir.cls}, \file{scrbook.cls} and % \file{scrarticle.cls} do the same as their corresponding standard classes, % and sometimes a little more, but what interests us here is pretty much the % same. See also the \pkg{appendix} package. % % The standard \cs{appendix} command is a one way switch, in other words, it % cannot be reverted (see \url{https://tex.stackexchange.com/a/444057}). So, % even if the fact that it is a ``switch'' rather than an environment % complicates things, because we have to make ungrouped settings to correspond % to its effects, in practice this is not a big deal, since these settings are % never really reverted (by default, at least). Hence, hooking into % \cs{appendix} is a viable and natural alternative. The \cls{memoir} class % and the \pkg{appendix} package define the \texttt{appendices} and % \texttt{subappendices} environments, which provide for a way for the % appendix to ``end'', but in this case, of course, we can hook into the % environment instead. % % \begin{macrocode} \@@_compat_module:nn { appendix } { \AddToHook { cmd / appendix / before } { \@@_zcsetup:n { countertype = { chapter = appendix , section = appendix , subsection = appendix , subsubsection = appendix , paragraph = appendix , subparagraph = appendix , } } } } % \end{macrocode} % % Depending on the definition of \cs{appendix}, using the hook may lead to % trouble with the first released version of \pkg{ltcmdhooks} (the one % released with the 2021-06-01 kernel). Particularly, if the definition of % the command being hooked at contains a double hash mark (\texttt{\#\#}) the % patch to add the hook, if it needs to be done with the \cs{scantokens} % method, may fail noisily (see \url{https://tex.stackexchange.com/q/617905}, % with a detailed explanation and possible workaround by \contributor{Phelype % Oleinik}). The 2021-11-15 kernel release already handles this gracefully, % thanks to fix by \contributor{Phelype Oleinik} at % \url{https://github.com/latex3/latex2e/pull/699}. % % % \subsection{\opt{appendices}} % % This module applies both to the \pkg{appendix} package, and to the % \cls{memoir} class, since it ``emulates'' the package. % % \begin{macrocode} \@@_compat_module:nn { appendices } { \@@_if_package_loaded:nT { appendix } { \newcounter { zc@appendix } \newcounter { zc@save@appendix } \setcounter { zc@appendix } { 0 } \setcounter { zc@save@appendix } { 0 } \cs_if_exist:cTF { chapter } { \@@_zcsetup:n { counterresetby = { chapter = zc@appendix } } } { \cs_if_exist:cT { section } { \@@_zcsetup:n { counterresetby = { section = zc@appendix } } } } \AddToHook { env / appendices / begin } { \stepcounter { zc@save@appendix } \setcounter { zc@appendix } { \value { zc@save@appendix } } \@@_zcsetup:n { countertype = { chapter = appendix , section = appendix , subsection = appendix , subsubsection = appendix , paragraph = appendix , subparagraph = appendix , } } } \AddToHook { env / appendices / end } { \setcounter { zc@appendix } { 0 } } \AddToHook { cmd / appendix / before } { \stepcounter { zc@save@appendix } \setcounter { zc@appendix } { \value { zc@save@appendix } } } \AddToHook { env / subappendices / begin } { \@@_zcsetup:n { countertype = { section = appendix , subsection = appendix , subsubsection = appendix , paragraph = appendix , subparagraph = appendix , } , } } \msg_info:nnn { zref-clever } { compat-package } { appendix } } } % \end{macrocode} % % % % \subsection{\opt{memoir}} % % The \cls{memoir} document class has quite a number of cross-referencing % related features, mostly dealing with captions, subfloats, and notes. Some % of them are implemented in ways which make difficult the use of \pkg{zref}, % particularly \cs{zlabel}, short of redefining the whole stuff ourselves. % Hopefully, these features are specialized enough to make \pkg{zref-clever} % useful enough with \cls{memoir} without much friction, but unless some % support is added upstream, it is difficult not to be a little intrusive % here. % % \begin{enumerate} % \item Caption functionality which receives \meta{label} as optional % argument, namely: % \begin{enumerate} % \item The \env{sidecaption} and \env{sidecontcaption} environments. These % environments \emph{store} the label in an internal macro, % \cs{m@mscaplabel}, at the begin environment code (more precisely in % \cs[replace=false]{@@sidecaption}), but both the call to % \cs{refstepcounter} and the expansion of \cs{m@mscaplabel} take place at % \cs{endsidecaption}. For this reason, hooks are not particularly % helpful, and there is not any easy way to grab the \meta{label} argument % to start with. I can see two ways to deal with these environments, none % of which I really like. First, map through \cs{m@mscaplabel} until % \cs{label} is found, then grab the next token which is the \meta{label}. % This can be used to set a \cs{zlabel} either with a kernel environment % hook, or with \cs{@mem@scap@afterhook} (the former requires running % \cs{refstepcounter} on our own, since the \texttt{env/.../end} hook % comes before this is done by \cs{endsidecaption}). Second, locally % redefine \cs{label} to set both labels inside the environments. % \item The bilingual caption commands: \cs{bitwonumcaption}, % \cs{bionenumcaption}, and \cs{bicaption}. These commands do not support % setting the label in their arguments (the labels do get set, but they % end up included in the \texttt{title} property of the label too). So we % do the same for them as for \env{sidecaption}, just taking care of % grouping, since we can't count on the convenience of the environment % hook (luckily for us, they are scoped themselves, so we can add an extra % group there). % \end{enumerate} % \item The \cs{subcaptionref} command, which makes a reference to the % subcaption without the number of the main caption (e.g. ``(b)'', instead % of ``2.3(b)''), for labels set inside the \meta{subtitle} argument of the % subcaptioning commands, namely: \cs{subcaption}, \cs{contsubcaption}, % \cs{subbottom}, \cs{contsubbottom}, \cs{subtop}. This functionality is % implemented by \cls{memoir} by setting a \emph{second label} with prefix % \texttt{sub@\meta{label}}, and storing there just that part of interest. % With \pkg{zref} this part is easier, since we can just add an extra % property and retrieve it later on. The thing is that it is hard to find a % place to hook into to add the property to the \texttt{main} list, since % \cls{memoir} does not really consider the possibility of some other % command setting labels. \cs{@memsubcaption} is the best place to hook I % could find. It is used by subcaptioning commands, and only those. And % there is no hope for an environment hook in this case anyway. % \item \cls{memoir}'s \cs{footnote}, \cs{verbfootnote}, \cs{sidefootnote} and % \cs{pagenote}, just as the regular \cs{footnote} until recently in the % kernel, do not set \cs{@currentcounter} alongside \cs{@currentlabel}, % proper referencing to them requires setting the type for it. % \item Note that \cls{memoir}'s appendix features ``emulates'' the % \pkg{appendix} package, hence the corresponding compatibility module is % loaded for \cls{memoir} even if that package is not itself loaded. The % same is true for the \cs{appendix} command module, since it is also % defined. % \end{enumerate} % % % \begin{macrocode} \@@_compat_module:nn { memoir } { \@@_if_class_loaded:nT { memoir } { % \end{macrocode} % Add subfigure and subtable support out of the box. Technically, this is not % ``default'' behavior for \cls{memoir}, users have to enable it with % \cs{newsubfloat}, but let this be smooth. Still, this does not cover any % other floats created with \cs{newfloat}. Also include setup for % \env{verse}. % \begin{macrocode} \@@_zcsetup:n { countertype = { subfigure = figure , subtable = table , poemline = line , } , counterresetby = { subfigure = figure , subtable = table , } , } % \end{macrocode} % Support for caption \cls{memoir} features that require that \meta{label} be % supplied as an optional argument. % \begin{macrocode} \cs_new_protected:Npn \@@_memoir_both_labels: { \cs_set_eq:NN \@@_memoir_orig_label:n \label \cs_set:Npn \@@_memoir_label_and_zlabel:n ##1 { \@@_memoir_orig_label:n {##1} \zlabel{##1} } \cs_set_eq:NN \label \@@_memoir_label_and_zlabel:n } \AddToHook { env / sidecaption / begin } { \@@_memoir_both_labels: } \AddToHook { env / sidecontcaption / begin } { \@@_memoir_both_labels: } \AddToHook{ cmd / bitwonumcaption / before } { \group_begin: \@@_memoir_both_labels: } \AddToHook{ cmd / bitwonumcaption / after } { \group_end: } \AddToHook{ cmd / bionenumcaption / before } { \group_begin: \@@_memoir_both_labels: } \AddToHook{ cmd / bionenumcaption / after } { \group_end: } \AddToHook{ cmd / bicaption / before } { \group_begin: \@@_memoir_both_labels: } \AddToHook{ cmd / bicaption / after } { \group_end: } % \end{macrocode} % Support for \texttt{subcaption} reference. % \begin{macrocode} \zref@newprop { subcaption } { \cs_if_exist_use:c { @@@@thesub \@captype } } \AddToHook{ cmd / @memsubcaption / before } { \zref@localaddprop \ZREF@mainlist { subcaption } } % \end{macrocode} % Support for \cs{footnote}, \cs{verbfootnote}, \cs{sidefootnote}, and % \cs{pagenote}. % \begin{macrocode} \tl_new:N \l_@@_memoir_footnote_type_tl \tl_set:Nn \l_@@_memoir_footnote_type_tl { footnote } \AddToHook { env / minipage / begin } { \tl_set:Nn \l_@@_memoir_footnote_type_tl { mpfootnote } } \AddToHook { cmd / @makefntext / before } { \@@_zcsetup:x { currentcounter = \l_@@_memoir_footnote_type_tl } } \AddToHook { cmd / @makesidefntext / before } { \@@_zcsetup:n { currentcounter = sidefootnote } } \@@_zcsetup:n { countertype = { sidefootnote = footnote , pagenote = endnote , } , } \AddToHook { file / \jobname.ent / before } { \@@_zcsetup:x { currentcounter = pagenote } } \msg_info:nnn { zref-clever } { compat-class } { memoir } } } % \end{macrocode} % % % % \subsection{\opt{KOMA}} % % Support for \texttt{KOMA-Script} document classes. % % \begin{macrocode} \@@_compat_module:nn { KOMA } { \cs_if_exist:NT \KOMAClassName { % \end{macrocode} % Add support for \env{captionbeside} and \env{captionofbeside} environments. % These environments \emph{do} run some variation of \cs{caption} and hence % \cs{refstepcounter}. However, this happens inside a parbox inside the % environment, thus grouped, such that we cannot see the variables set by % \cs{refstepcounter} when we are setting the label. \cs{@currentlabel} is % smuggled out of the group by KOMA, but the same care is not granted for % \cs{@currentcounter}. So we have to rely on \cs{@captype}, which the % underlying caption infrastructure feeds to \cs{refstepcounter}. Since we % must use \texttt{env/.../after} hooks, care should be taken not to set the % \opt{currentcounter} option unscoped, which would be quite disastrous. For % this reason, though more ``invasive'', we set \cs{@currentcounter} instead, % which at least will be set straight the next time \cs{refstepcounter} runs. % It sounds reasonable, it is the same treatment \cs{@currentlabel} is % receiving in this case. % \begin{macrocode} \AddToHook { env / captionbeside / after } { \tl_if_exist:NT \@captype { \tl_set_eq:NN \@currentcounter \@captype } } \tl_new:N \g_@@_koma_captionofbeside_captype_tl \AddToHook { env / captionofbeside / end } { \tl_gset_eq:NN \g_@@_koma_captype_tl \@captype } \AddToHook { env / captionofbeside / after } { \tl_if_eq:NnF \@currenvir { document } { \tl_if_empty:NF \g_@@_koma_captype_tl { \tl_set_eq:NN \@currentcounter \g_@@_koma_captype_tl } } \tl_gclear:N \g_@@_koma_captype_tl } \msg_info:nnx { zref-clever } { compat-class } { \KOMAClassName } } } % \end{macrocode} % % % % \subsection{\opt{amsmath}} % % About this, see \url{https://tex.stackexchange.com/a/402297}. % % \begin{macrocode} \@@_compat_module:nn { amsmath } { \@@_if_package_loaded:nT { amsmath } { % \end{macrocode} % First, we define a function for label setting inside \pkg{amsmath} math % environments, we want it to set both \cs{zlabel} and \cs{label}. We may % ``get a ride'', but not steal the place altogether. This makes for % potentially redundant labels, but seems a good compromise. We \emph{must} % use the lower level \cs{zref@label} in this context, and hence also handle % protection with \cs{zref@wrapper@babel}, because \cs{zlabel} makes itself % no-op when \cs{label} is equal to \cs{ltx@gobble}, and that's precisely the % case inside the \env{multline} environment (and, damn!, I took a beating of % this detail\dots{}). % \begin{macrocode} \cs_set_nopar:Npn \@@_ltxlabel:n #1 { \@@_orig_ltxlabel:n {#1} \zref@wrapper@babel \zref@label {#1} } % \end{macrocode} % Then we must store the original value of \cs{ltx@label}, which is the macro % actually responsible for setting the labels inside \pkg{amsmath}'s math % environments. And, after that, redefine it to be \cs{@@_ltxlabel:n} % instead. We must handle \pkg{hyperref} here, which comes very late in the % preamble, and which loads \pkg{nameref} at \texttt{begindocument}, which in % turn, lets \cs{ltx@label} be \cs{label}. This has to come after % \pkg{nameref}. Other classes packages also redefine \cs{ltx@label}, which % may cause some trouble. A \texttt{grep} on \texttt{texmf-dist} returns hits % for: \file{thm-restate.sty}, \file{smartref.sty}, \file{jmlrbook.cls}, % \file{cleveref.sty}, \file{cryptocode.sty}, \file{nameref.sty}, % \file{easyeqn.sty}, \file{empheq.sty}, \file{ntheorem.sty}, % \file{nccmath.sty}, \file{nwejm.cls}, \file{nwejmart.cls}, % \file{aguplus.sty}, \file{aguplus.cls}, \file{agupp.sty}, % \file{amsmath.hyp}, \file{amsmath.sty} (surprise!), \file{amsmath.4ht}, % \file{nameref.4ht}, \file{frenchle.sty}, \file{french.sty}, plus % corresponding documentations and different versions of the same packages. % That's not too many, but not ``just a few'' either. The critical ones are % explicitly handled here (\pkg{amsmath} itself, and \pkg{nameref}). A number % of those I'm really not acquainted with. For \pkg{cleveref}, in particular, % this procedure is not compatible with it. If we happen to come later than % it and override its definition, this may be a substantial problem for % \pkg{cleveref}, since it will find the label, but it won't contain the data % it is expecting. However, this should normally not occur, if the user has % followed the documented recommendation for \pkg{cleveref} to load it last, % or at least very late, and besides I don't see much of an use case for using % both \pkg{cleveref} and \pkg{zref-clever} together. I have documented in % the user manual that this module may cause potential issues, and how to work % around them. And I have made an upstream feature request for a hook, so % that this could be made more cleanly at % \url{https://github.com/latex3/hyperref/issues/212}. % \begin{macrocode} \@@_if_package_loaded:nTF { hyperref } { \AddToHook { package / nameref / after } { \cs_new_eq:NN \@@_orig_ltxlabel:n \ltx@label \cs_set_eq:NN \ltx@label \@@_ltxlabel:n } } { \cs_new_eq:NN \@@_orig_ltxlabel:n \ltx@label \cs_set_eq:NN \ltx@label \@@_ltxlabel:n } % \end{macrocode} % The \env{subequations} environment uses \texttt{parentequation} and % \texttt{equation} as counters, but only the later is subject to % \cs{refstepcounter}. What happens is: at the start, \texttt{equation} is % refstepped, it is then stored in \texttt{parentequation} and set to `0' and, % at the end of the environment it is restored to the value of % \texttt{parentequation}. We cannot even set \cs{@currentcounter} at % \texttt{env/.../begin}, since the call to % \cs{refstepcounter}\texttt{\{equation\}} done by \env{subequations} will % override that in sequence. Unfortunately, the suggestion to set % \cs{@currentcounter} to \texttt{parentequation} here was not accepted, see % \url{https://github.com/latex3/latex2e/issues/687#issuecomment-951451024} % and subsequent discussion. So, for \env{subequations}, we really must % specify manually \opt{currentcounter} and the resetting. Note that, for % \env{subequations}, \cs{zlabel} works just fine (that is, if given % immediately after \texttt{\\begin\{subequations\}}, to refer to the % parent equation). % \begin{macrocode} \AddToHook { env / subequations / begin } { \@@_zcsetup:x { counterresetby = { parentequation = \@@_counter_reset_by:n { equation } , equation = parentequation , } , currentcounter = parentequation , countertype = { parentequation = equation } , } } % \end{macrocode} % \pkg{amsmath} does use \cs{refstepcounter} for the \texttt{equation} counter % throughout and does set \cs{@currentcounter} for \cs{tag}s. But we still % have to manually reset \opt{currentcounter} to default because, since we had % to manually set \opt{currentcounter} to \texttt{parentequation} in % \env{subequations}, we also have to manually set it to \env{equation} in % environments which may be used within it. The \env{xxalignat} environment % is not included, because it is ``starred'' by default (i.e.\ unnumbered), % and does not display or accepts labels or tags anyway. The \env{-ed} % (\env{gathered}, \env{aligned}, and \env{alignedat}) and \env{cases} % environments ``must appear within an enclosing math environment''. Same % logic applies to other environments defined or redefined by the package, % like \env{array}, \env{matrix} and variations. Finally, \env{split} too can % only be used as part of another environment. % \begin{macrocode} \clist_map_inline:nn { equation , equation* , align , align* , alignat , alignat* , flalign , flalign* , xalignat , xalignat* , gather , gather* , multline , multline* , } { \AddToHook { env / #1 / begin } { \@@_zcsetup:n { currentcounter = equation } } } % \end{macrocode} % And a last touch of care for \pkg{amsmath}'s refinements: make the equation % references \cs{textup}. % \begin{macrocode} \zcRefTypeSetup { equation } { reffont = \upshape , preref = {\textup{(}} , postref = {\textup{)}} , } \msg_info:nnn { zref-clever } { compat-package } { amsmath } } } % \end{macrocode} % % % % \subsection{\opt{mathtools}} % % All math environments defined by \pkg{mathtools}, extending the % \pkg{amsmath} set, are meant to be used within enclosing math environments, % hence we don't need to handle them specially, since the numbering and the % counting is being done on the side of \pkg{amsmath}. This includes the new % \env{cases} and \env{matrix} variants, and also \env{multlined}. % % Hence, as far as I can tell, the only cross-reference related feature to % deal with is the \opt{showonlyrefs} option, whose machinery involves writing % an extra internal label to the \file{.aux} file to track for labels which % get actually referred to. This is a little more involved, and implies in % doing special handling inside \cs{zcref}, but the feature is very cool, so % it's worth it. % % \begin{macrocode} \bool_new:N \l_@@_mathtools_showonlyrefs_bool \@@_compat_module:nn { mathtools } { \@@_if_package_loaded:nT { mathtools } { \MH_if_boolean:nT { show_only_refs } { \bool_set_true:N \l_@@_mathtools_showonlyrefs_bool \cs_new_protected:Npn \@@_mathtools_showonlyrefs:n #1 { \@bsphack \seq_map_inline:Nn #1 { \exp_args:Nx \tl_if_eq:nnTF { \@@_extract_unexp:nnn {##1} { zc@type } { } } { equation } { \protected@write \@auxout { } { \string \MT@newlabel {##1} } } { \exp_args:Nx \tl_if_eq:nnT { \@@_extract_unexp:nnn {##1} { zc@type } { } } { parentequation } { \protected@write \@auxout { } { \string \MT@newlabel {##1} } } } } \@esphack } \msg_info:nnn { zref-clever } { compat-package } { mathtools } } } } % \end{macrocode} % % % \subsection{\opt{breqn}} % % From the \pkg{breqn} documentation: \textquote{Use of the normal \cs{label} % command instead of the \opt{label} option works, I think, most of the time % (untested)}. Indeed, light testing suggests it does work for \cs{zlabel} % just as well. However, if it happens not to work, there was no easy % alternative handle I could find. In particular, it does not seem viable to % leverage the \opt{label=} option without hacking the package internals, even % if the case of doing so would not be specially tricky, just ``not very % civil''. % % \begin{macrocode} \@@_compat_module:nn { breqn } { \@@_if_package_loaded:nT { breqn } { % \end{macrocode} % Contrary to the practice in \pkg{amsmath}, which prints \cs{tag} even in % unnumbered environments, the starred environments from \pkg{breqn} don't % typeset any tag/number at all, even for a manually given \opt{number=} as an % option. So, even if one can actually set a label in them, it is not really % meaningful to make a reference to them. Also contrary to \pkg{amsmath}'s % practice, \pkg{breqn} uses \cs{stepcounter} instead of \cs{refstepcounter} % for incrementing the equation counters (see % \url{https://tex.stackexchange.com/a/241150}). % \begin{macrocode} \AddToHook { env / dgroup / begin } { \@@_zcsetup:x { counterresetby = { parentequation = \@@_counter_reset_by:n { equation } , equation = parentequation , } , currentcounter = parentequation , countertype = { parentequation = equation } , } } \clist_map_inline:nn { dmath , dseries , darray , } { \AddToHook { env / #1 / begin } { \@@_zcsetup:n { currentcounter = equation } } } \msg_info:nnn { zref-clever } { compat-package } { breqn } } } % \end{macrocode} % % % % \subsection{\opt{listings}} % % \begin{macrocode} \@@_compat_module:nn { listings } { \@@_if_package_loaded:nT { listings } { \@@_zcsetup:n { countertype = { lstlisting = listing , lstnumber = line , } , counterresetby = { lstnumber = lstlisting } , } % \end{macrocode} % Set (also) a \cs{zlabel} with the label received in the \texttt{label=} % option from the \texttt{lstlisting} environment. The \emph{only} place to % set this label is the \texttt{PreInit} hook. This hook, comes right after % \cs{lst@MakeCaption} in \cs{lst@Init}, which runs \cs{refstepcounter} on % \texttt{lstlisting}, so we must come after it. Also \pkg{listings} itself % sets \cs{@currentlabel} to \cs{thelstnumber} in the \texttt{Init} hook, % which comes right after the \texttt{PreInit} one in \cs{lst@Init}. Since, % if we add to \texttt{Init} here, we go to the end of it, we'd be seeing the % wrong \cs{@currentlabel} at that point. % \begin{macrocode} \lst@AddToHook { PreInit } { \tl_if_empty:NF \lst@label { \zlabel { \lst@label } } } % \end{macrocode} % Set \texttt{currentcounter} to \texttt{lstnumber} in the \texttt{Init} hook, % since \pkg{listings} itself sets \cs{@currentlabel} to \cs{thelstnumber} % here. Note that \pkg{listings} \emph{does use} \cs{refstepcounter} on % \texttt{lstnumber}, but does so in the \texttt{EveryPar} hook, and there % must be some grouping involved such that \cs{@currentcounter} ends up not % being visible to the label. See section ``Line numbers'' of `\texttt{texdoc % listings-devel}' (the \file{.dtx}), and search for the definition of macro % \cs{c@lstnumber}. Indeed, the fact that \pkg{listings} manually sets % \cs{@currentlabel} to \cs{thelstnumber} is a signal that the work of % \cs{refstepcounter} is being restrained somehow. % \begin{macrocode} \lst@AddToHook { Init } { \@@_zcsetup:n { currentcounter = lstnumber } } \msg_info:nnn { zref-clever } { compat-package } { listings } } } % \end{macrocode} % % % \subsection{\opt{enumitem}} % % The procedure below will ``see'' any changes made to the \texttt{enumerate} % environment (made with \pkg{enumitem}'s \cs{renewlist}) as long as it is % done in the preamble. Though, technically, \cs{renewlist} can be issued % anywhere in the document, this should be more than enough for the purpose at % hand. Besides, trying to retrieve this information ``on the fly'' would be % much overkill. % % The only real reason to ``renew'' \texttt{enumerate} itself is to change % \marg{max-depth}. \cs{renewlist} \emph{hard-codes} \texttt{max-depth} in % the environment's definition (well, just as the kernel does), so we cannot % retrieve this information from any sort of variable. But \cs{renewlist} % also creates any needed missing counters, so we can use their existence to % make the appropriate settings. In the end, the existence of the counters is % indeed what matters from \pkg{zref-clever}'s perspective. Since the first % four are defined by the kernel and already setup for \pkg{zref-clever} by % default, we start from \(5\), and stop at the first non-existent % \cs[no-index]{c@enumN} counter. % % \begin{macrocode} \@@_compat_module:nn { enumitem } { \@@_if_package_loaded:nT { enumitem } { \int_set:Nn \l_tmpa_int { 5 } \bool_while_do:nn { \cs_if_exist_p:c { c@ enum \int_to_roman:n { \l_tmpa_int } } } { \@@_zcsetup:x { counterresetby = { enum \int_to_roman:n { \l_tmpa_int } = enum \int_to_roman:n { \l_tmpa_int - 1 } } , countertype = { enum \int_to_roman:n { \l_tmpa_int } = item } , } \int_incr:N \l_tmpa_int } \int_compare:nNnT { \l_tmpa_int } > { 5 } { \msg_info:nnn { zref-clever } { compat-package } { enumitem } } } } % \end{macrocode} % % % % \subsection{\opt{subcaption}} % % % \begin{macrocode} \@@_compat_module:nn { subcaption } { \@@_if_package_loaded:nT { subcaption } { \@@_zcsetup:n { countertype = { subfigure = figure , subtable = table , } , counterresetby = { subfigure = figure , subtable = table , } , } % \end{macrocode} % Support for \texttt{subref} reference. % \begin{macrocode} \zref@newprop { subref } { \cs_if_exist_use:c { thesub \@captype } } \tl_put_right:Nn \caption@subtypehook { \zref@localaddprop \ZREF@mainlist { subref } } } } % \end{macrocode} % % % \subsection{\opt{subfig}} % % Though \pkg{subfig} offers \cs{subref} (as \pkg{subcaption}), I could not % find any reasonable place to add the \texttt{subref} property to % \pkg{zref}'s main list. % % \begin{macrocode} \@@_compat_module:nn { subfig } { \@@_if_package_loaded:nT { subfig } { \@@_zcsetup:n { countertype = { subfigure = figure , subtable = table , } , counterresetby = { subfigure = figure , subtable = table , } , } } } % \end{macrocode} % % % \begin{macrocode} % % \end{macrocode} % % % % \section{Language files} % % Initial values for the English, German, French, Portuguese, and Spanish, % language files have been provided by the author. Translations available for % document elements' names in other packages have been an useful reference for % the purpose, namely: \pkg{babel}, \pkg{cleveref}, \pkg{translator}, and % \pkg{translations}. % % % \subsection{English} % % English language file has been initially provided by the author. % % \begin{macrocode} %<*package> \zcDeclareLanguage { english } \zcDeclareLanguageAlias { american } { english } \zcDeclareLanguageAlias { australian } { english } \zcDeclareLanguageAlias { british } { english } \zcDeclareLanguageAlias { canadian } { english } \zcDeclareLanguageAlias { newzealand } { english } \zcDeclareLanguageAlias { UKenglish } { english } \zcDeclareLanguageAlias { USenglish } { english } % % \end{macrocode} % % \begin{macrocode} %<*lang-english> % \end{macrocode} % % \begin{macrocode} namesep = {\nobreakspace} , pairsep = {~and\nobreakspace} , listsep = {,~} , lastsep = {~and\nobreakspace} , tpairsep = {~and\nobreakspace} , tlistsep = {,~} , tlastsep = {,~and\nobreakspace} , notesep = {~} , rangesep = {~to\nobreakspace} , type = book , Name-sg = Book , name-sg = book , Name-pl = Books , name-pl = books , type = part , Name-sg = Part , name-sg = part , Name-pl = Parts , name-pl = parts , type = chapter , Name-sg = Chapter , name-sg = chapter , Name-pl = Chapters , name-pl = chapters , type = section , Name-sg = Section , name-sg = section , Name-pl = Sections , name-pl = sections , type = paragraph , Name-sg = Paragraph , name-sg = paragraph , Name-pl = Paragraphs , name-pl = paragraphs , Name-sg-ab = Par. , name-sg-ab = par. , Name-pl-ab = Par. , name-pl-ab = par. , type = appendix , Name-sg = Appendix , name-sg = appendix , Name-pl = Appendices , name-pl = appendices , type = page , Name-sg = Page , name-sg = page , Name-pl = Pages , name-pl = pages , rangesep = {\textendash} , type = line , Name-sg = Line , name-sg = line , Name-pl = Lines , name-pl = lines , type = figure , Name-sg = Figure , name-sg = figure , Name-pl = Figures , name-pl = figures , Name-sg-ab = Fig. , name-sg-ab = fig. , Name-pl-ab = Figs. , name-pl-ab = figs. , type = table , Name-sg = Table , name-sg = table , Name-pl = Tables , name-pl = tables , type = item , Name-sg = Item , name-sg = item , Name-pl = Items , name-pl = items , type = footnote , Name-sg = Footnote , name-sg = footnote , Name-pl = Footnotes , name-pl = footnotes , type = endnote , Name-sg = Note , name-sg = note , Name-pl = Notes , name-pl = notes , type = note , Name-sg = Note , name-sg = note , Name-pl = Notes , name-pl = notes , type = equation , Name-sg = Equation , name-sg = equation , Name-pl = Equations , name-pl = equations , Name-sg-ab = Eq. , name-sg-ab = eq. , Name-pl-ab = Eqs. , name-pl-ab = eqs. , preref = {(} , postref = {)} , type = theorem , Name-sg = Theorem , name-sg = theorem , Name-pl = Theorems , name-pl = theorems , type = lemma , Name-sg = Lemma , name-sg = lemma , Name-pl = Lemmas , name-pl = lemmas , type = corollary , Name-sg = Corollary , name-sg = corollary , Name-pl = Corollaries , name-pl = corollaries , type = proposition , Name-sg = Proposition , name-sg = proposition , Name-pl = Propositions , name-pl = propositions , type = definition , Name-sg = Definition , name-sg = definition , Name-pl = Definitions , name-pl = definitions , type = proof , Name-sg = Proof , name-sg = proof , Name-pl = Proofs , name-pl = proofs , type = result , Name-sg = Result , name-sg = result , Name-pl = Results , name-pl = results , type = remark , Name-sg = Remark , name-sg = remark , Name-pl = Remarks , name-pl = remarks , type = example , Name-sg = Example , name-sg = example , Name-pl = Examples , name-pl = examples , type = algorithm , Name-sg = Algorithm , name-sg = algorithm , Name-pl = Algorithms , name-pl = algorithms , type = listing , Name-sg = Listing , name-sg = listing , Name-pl = Listings , name-pl = listings , type = exercise , Name-sg = Exercise , name-sg = exercise , Name-pl = Exercises , name-pl = exercises , type = solution , Name-sg = Solution , name-sg = solution , Name-pl = Solutions , name-pl = solutions , % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % % \subsection{German} % % German language file has been initially provided by the author. % % \begin{macrocode} %<*package> \zcDeclareLanguage [ declension = { N , A , D , G } , gender = { f , m , n } , allcaps ] { german } \zcDeclareLanguageAlias { austrian } { german } \zcDeclareLanguageAlias { germanb } { german } \zcDeclareLanguageAlias { ngerman } { german } \zcDeclareLanguageAlias { naustrian } { german } \zcDeclareLanguageAlias { nswissgerman } { german } \zcDeclareLanguageAlias { swissgerman } { german } % % \end{macrocode} % % \begin{macrocode} %<*lang-german> % \end{macrocode} % % \begin{macrocode} namesep = {\nobreakspace} , pairsep = {~und\nobreakspace} , listsep = {,~} , lastsep = {~und\nobreakspace} , tpairsep = {~und\nobreakspace} , tlistsep = {,~} , tlastsep = {~und\nobreakspace} , notesep = {~} , rangesep = {~bis\nobreakspace} , type = book , gender = n , case = N , Name-sg = Buch , Name-pl = Bücher , case = A , Name-sg = Buch , Name-pl = Bücher , case = D , Name-sg = Buch , Name-pl = Büchern , case = G , Name-sg = Buches , Name-pl = Bücher , type = part , gender = m , case = N , Name-sg = Teil , Name-pl = Teile , case = A , Name-sg = Teil , Name-pl = Teile , case = D , Name-sg = Teil , Name-pl = Teilen , case = G , Name-sg = Teiles , Name-pl = Teile , type = chapter , gender = n , case = N , Name-sg = Kapitel , Name-pl = Kapitel , case = A , Name-sg = Kapitel , Name-pl = Kapitel , case = D , Name-sg = Kapitel , Name-pl = Kapiteln , case = G , Name-sg = Kapitels , Name-pl = Kapitel , type = section , gender = m , case = N , Name-sg = Abschnitt , Name-pl = Abschnitte , case = A , Name-sg = Abschnitt , Name-pl = Abschnitte , case = D , Name-sg = Abschnitt , Name-pl = Abschnitten , case = G , Name-sg = Abschnitts , Name-pl = Abschnitte , type = paragraph , gender = m , case = N , Name-sg = Absatz , Name-pl = Absätze , case = A , Name-sg = Absatz , Name-pl = Absätze , case = D , Name-sg = Absatz , Name-pl = Absätzen , case = G , Name-sg = Absatzes , Name-pl = Absätze , type = appendix , gender = m , case = N , Name-sg = Anhang , Name-pl = Anhänge , case = A , Name-sg = Anhang , Name-pl = Anhänge , case = D , Name-sg = Anhang , Name-pl = Anhängen , case = G , Name-sg = Anhangs , Name-pl = Anhänge , type = page , gender = f , case = N , Name-sg = Seite , Name-pl = Seiten , case = A , Name-sg = Seite , Name-pl = Seiten , case = D , Name-sg = Seite , Name-pl = Seiten , case = G , Name-sg = Seite , Name-pl = Seiten , rangesep = {\textendash} , type = line , gender = f , case = N , Name-sg = Zeile , Name-pl = Zeilen , case = A , Name-sg = Zeile , Name-pl = Zeilen , case = D , Name-sg = Zeile , Name-pl = Zeilen , case = G , Name-sg = Zeile , Name-pl = Zeilen , type = figure , gender = f , case = N , Name-sg = Abbildung , Name-pl = Abbildungen , Name-sg-ab = Abb. , Name-pl-ab = Abb. , case = A , Name-sg = Abbildung , Name-pl = Abbildungen , Name-sg-ab = Abb. , Name-pl-ab = Abb. , case = D , Name-sg = Abbildung , Name-pl = Abbildungen , Name-sg-ab = Abb. , Name-pl-ab = Abb. , case = G , Name-sg = Abbildung , Name-pl = Abbildungen , Name-sg-ab = Abb. , Name-pl-ab = Abb. , type = table , gender = f , case = N , Name-sg = Tabelle , Name-pl = Tabellen , case = A , Name-sg = Tabelle , Name-pl = Tabellen , case = D , Name-sg = Tabelle , Name-pl = Tabellen , case = G , Name-sg = Tabelle , Name-pl = Tabellen , type = item , gender = m , case = N , Name-sg = Punkt , Name-pl = Punkte , case = A , Name-sg = Punkt , Name-pl = Punkte , case = D , Name-sg = Punkt , Name-pl = Punkten , case = G , Name-sg = Punktes , Name-pl = Punkte , type = footnote , gender = f , case = N , Name-sg = Fußnote , Name-pl = Fußnoten , case = A , Name-sg = Fußnote , Name-pl = Fußnoten , case = D , Name-sg = Fußnote , Name-pl = Fußnoten , case = G , Name-sg = Fußnote , Name-pl = Fußnoten , type = endnote , gender = f , case = N , Name-sg = Endnote , Name-pl = Endnoten , case = A , Name-sg = Endnote , Name-pl = Endnoten , case = D , Name-sg = Endnote , Name-pl = Endnoten , case = G , Name-sg = Endnote , Name-pl = Endnoten , type = note , gender = f , case = N , Name-sg = Anmerkung , Name-pl = Anmerkungen , case = A , Name-sg = Anmerkung , Name-pl = Anmerkungen , case = D , Name-sg = Anmerkung , Name-pl = Anmerkungen , case = G , Name-sg = Anmerkung , Name-pl = Anmerkungen , type = equation , gender = f , case = N , Name-sg = Gleichung , Name-pl = Gleichungen , case = A , Name-sg = Gleichung , Name-pl = Gleichungen , case = D , Name-sg = Gleichung , Name-pl = Gleichungen , case = G , Name-sg = Gleichung , Name-pl = Gleichungen , preref = {(} , postref = {)} , type = theorem , gender = n , case = N , Name-sg = Theorem , Name-pl = Theoreme , case = A , Name-sg = Theorem , Name-pl = Theoreme , case = D , Name-sg = Theorem , Name-pl = Theoremen , case = G , Name-sg = Theorems , Name-pl = Theoreme , type = lemma , gender = n , case = N , Name-sg = Lemma , Name-pl = Lemmata , case = A , Name-sg = Lemma , Name-pl = Lemmata , case = D , Name-sg = Lemma , Name-pl = Lemmata , case = G , Name-sg = Lemmas , Name-pl = Lemmata , type = corollary , gender = n , case = N , Name-sg = Korollar , Name-pl = Korollare , case = A , Name-sg = Korollar , Name-pl = Korollare , case = D , Name-sg = Korollar , Name-pl = Korollaren , case = G , Name-sg = Korollars , Name-pl = Korollare , type = proposition , gender = m , case = N , Name-sg = Satz , Name-pl = Sätze , case = A , Name-sg = Satz , Name-pl = Sätze , case = D , Name-sg = Satz , Name-pl = Sätzen , case = G , Name-sg = Satzes , Name-pl = Sätze , type = definition , gender = f , case = N , Name-sg = Definition , Name-pl = Definitionen , case = A , Name-sg = Definition , Name-pl = Definitionen , case = D , Name-sg = Definition , Name-pl = Definitionen , case = G , Name-sg = Definition , Name-pl = Definitionen , type = proof , gender = m , case = N , Name-sg = Beweis , Name-pl = Beweise , case = A , Name-sg = Beweis , Name-pl = Beweise , case = D , Name-sg = Beweis , Name-pl = Beweisen , case = G , Name-sg = Beweises , Name-pl = Beweise , type = result , gender = n , case = N , Name-sg = Ergebnis , Name-pl = Ergebnisse , case = A , Name-sg = Ergebnis , Name-pl = Ergebnisse , case = D , Name-sg = Ergebnis , Name-pl = Ergebnissen , case = G , Name-sg = Ergebnisses , Name-pl = Ergebnisse , type = remark , gender = f , case = N , Name-sg = Bemerkung , Name-pl = Bemerkungen , case = A , Name-sg = Bemerkung , Name-pl = Bemerkungen , case = D , Name-sg = Bemerkung , Name-pl = Bemerkungen , case = G , Name-sg = Bemerkung , Name-pl = Bemerkungen , type = example , gender = n , case = N , Name-sg = Beispiel , Name-pl = Beispiele , case = A , Name-sg = Beispiel , Name-pl = Beispiele , case = D , Name-sg = Beispiel , Name-pl = Beispielen , case = G , Name-sg = Beispiels , Name-pl = Beispiele , type = algorithm , gender = m , case = N , Name-sg = Algorithmus , Name-pl = Algorithmen , case = A , Name-sg = Algorithmus , Name-pl = Algorithmen , case = D , Name-sg = Algorithmus , Name-pl = Algorithmen , case = G , Name-sg = Algorithmus , Name-pl = Algorithmen , type = listing , gender = n , case = N , Name-sg = Listing , Name-pl = Listings , case = A , Name-sg = Listing , Name-pl = Listings , case = D , Name-sg = Listing , Name-pl = Listings , case = G , Name-sg = Listings , Name-pl = Listings , type = exercise , gender = f , case = N , Name-sg = Übungsaufgabe , Name-pl = Übungsaufgaben , case = A , Name-sg = Übungsaufgabe , Name-pl = Übungsaufgaben , case = D , Name-sg = Übungsaufgabe , Name-pl = Übungsaufgaben , case = G , Name-sg = Übungsaufgabe , Name-pl = Übungsaufgaben , type = solution , gender = f , case = N , Name-sg = Lösung , Name-pl = Lösungen , case = A , Name-sg = Lösung , Name-pl = Lösungen , case = D , Name-sg = Lösung , Name-pl = Lösungen , case = G , Name-sg = Lösung , Name-pl = Lösungen , % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % % \subsection{French} % % French language file has been initially provided by the author, and has been % improved thanks to Denis Bitouzé and François Lagarde (at \githubissue{1}) % and participants of the Groupe francophone des Utilisateurs de \TeX{} % (GUTenberg) (at \url{https://groups.google.com/g/gut_fr/c/rNLm6weGcyg}) and % the \texttt{fr.comp.text.tex} (at % \url{https://groups.google.com/g/fr.comp.text.tex/c/Fa11Tf6MFFs}) mailing % lists. % % \begin{macrocode} %<*package> \zcDeclareLanguage [ gender = { f , m } ] { french } \zcDeclareLanguageAlias { acadian } { french } \zcDeclareLanguageAlias { canadien } { french } \zcDeclareLanguageAlias { francais } { french } \zcDeclareLanguageAlias { frenchb } { french } % % \end{macrocode} % % \begin{macrocode} %<*lang-french> % \end{macrocode} % % \begin{macrocode} namesep = {\nobreakspace} , pairsep = {~et\nobreakspace} , listsep = {,~} , lastsep = {~et\nobreakspace} , tpairsep = {~et\nobreakspace} , tlistsep = {,~} , tlastsep = {~et\nobreakspace} , notesep = {~} , rangesep = {~à\nobreakspace} , type = book , gender = m , Name-sg = Livre , name-sg = livre , Name-pl = Livres , name-pl = livres , type = part , gender = f , Name-sg = Partie , name-sg = partie , Name-pl = Parties , name-pl = parties , type = chapter , gender = m , Name-sg = Chapitre , name-sg = chapitre , Name-pl = Chapitres , name-pl = chapitres , type = section , gender = f , Name-sg = Section , name-sg = section , Name-pl = Sections , name-pl = sections , type = paragraph , gender = m , Name-sg = Paragraphe , name-sg = paragraphe , Name-pl = Paragraphes , name-pl = paragraphes , type = appendix , gender = f , Name-sg = Annexe , name-sg = annexe , Name-pl = Annexes , name-pl = annexes , type = page , gender = f , Name-sg = Page , name-sg = page , Name-pl = Pages , name-pl = pages , rangesep = {-} , type = line , gender = f , Name-sg = Ligne , name-sg = ligne , Name-pl = Lignes , name-pl = lignes , type = figure , gender = f , Name-sg = Figure , name-sg = figure , Name-pl = Figures , name-pl = figures , type = table , gender = f , Name-sg = Table , name-sg = table , Name-pl = Tables , name-pl = tables , type = item , gender = m , Name-sg = Point , name-sg = point , Name-pl = Points , name-pl = points , type = footnote , gender = f , Name-sg = Note , name-sg = note , Name-pl = Notes , name-pl = notes , type = endnote , gender = f , Name-sg = Note , name-sg = note , Name-pl = Notes , name-pl = notes , type = note , gender = f , Name-sg = Note , name-sg = note , Name-pl = Notes , name-pl = notes , type = equation , gender = f , Name-sg = Équation , name-sg = équation , Name-pl = Équations , name-pl = équations , preref = {(} , postref = {)} , type = theorem , gender = m , Name-sg = Théorème , name-sg = théorème , Name-pl = Théorèmes , name-pl = théorèmes , type = lemma , gender = m , Name-sg = Lemme , name-sg = lemme , Name-pl = Lemmes , name-pl = lemmes , type = corollary , gender = m , Name-sg = Corollaire , name-sg = corollaire , Name-pl = Corollaires , name-pl = corollaires , type = proposition , gender = f , Name-sg = Proposition , name-sg = proposition , Name-pl = Propositions , name-pl = propositions , type = definition , gender = f , Name-sg = Définition , name-sg = définition , Name-pl = Définitions , name-pl = définitions , type = proof , gender = f , Name-sg = Démonstration , name-sg = démonstration , Name-pl = Démonstrations , name-pl = démonstrations , type = result , gender = m , Name-sg = Résultat , name-sg = résultat , Name-pl = Résultats , name-pl = résultats , type = remark , gender = f , Name-sg = Remarque , name-sg = remarque , Name-pl = Remarques , name-pl = remarques , type = example , gender = m , Name-sg = Exemple , name-sg = exemple , Name-pl = Exemples , name-pl = exemples , type = algorithm , gender = m , Name-sg = Algorithme , name-sg = algorithme , Name-pl = Algorithmes , name-pl = algorithmes , type = listing , gender = m , Name-sg = Listing , name-sg = listing , Name-pl = Listings , name-pl = listings , type = exercise , gender = m , Name-sg = Exercice , name-sg = exercice , Name-pl = Exercices , name-pl = exercices , type = solution , gender = f , Name-sg = Solution , name-sg = solution , Name-pl = Solutions , name-pl = solutions , % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % % \subsection{Portuguese} % % Portuguese language file provided by the author, who's a native speaker of % (Brazilian) Portuguese. I do expect this to be sufficiently general, but if % Portuguese speakers from other places feel the need for a Portuguese % variant, please let me know. % % \begin{macrocode} %<*package> \zcDeclareLanguage [ gender = { f , m } ] { portuguese } \zcDeclareLanguageAlias { brazilian } { portuguese } \zcDeclareLanguageAlias { brazil } { portuguese } \zcDeclareLanguageAlias { portuges } { portuguese } % % \end{macrocode} % % \begin{macrocode} %<*lang-portuguese> % \end{macrocode} % % \begin{macrocode} namesep = {\nobreakspace} , pairsep = {~e\nobreakspace} , listsep = {,~} , lastsep = {~e\nobreakspace} , tpairsep = {~e\nobreakspace} , tlistsep = {,~} , tlastsep = {~e\nobreakspace} , notesep = {~} , rangesep = {~a\nobreakspace} , type = book , gender = m , Name-sg = Livro , name-sg = livro , Name-pl = Livros , name-pl = livros , type = part , gender = f , Name-sg = Parte , name-sg = parte , Name-pl = Partes , name-pl = partes , type = chapter , gender = m , Name-sg = Capítulo , name-sg = capítulo , Name-pl = Capítulos , name-pl = capítulos , type = section , gender = f , Name-sg = Seção , name-sg = seção , Name-pl = Seções , name-pl = seções , type = paragraph , gender = m , Name-sg = Parágrafo , name-sg = parágrafo , Name-pl = Parágrafos , name-pl = parágrafos , Name-sg-ab = Par. , name-sg-ab = par. , Name-pl-ab = Par. , name-pl-ab = par. , type = appendix , gender = m , Name-sg = Apêndice , name-sg = apêndice , Name-pl = Apêndices , name-pl = apêndices , type = page , gender = f , Name-sg = Página , name-sg = página , Name-pl = Páginas , name-pl = páginas , rangesep = {\textendash} , type = line , gender = f , Name-sg = Linha , name-sg = linha , Name-pl = Linhas , name-pl = linhas , type = figure , gender = f , Name-sg = Figura , name-sg = figura , Name-pl = Figuras , name-pl = figuras , Name-sg-ab = Fig. , name-sg-ab = fig. , Name-pl-ab = Figs. , name-pl-ab = figs. , type = table , gender = f , Name-sg = Tabela , name-sg = tabela , Name-pl = Tabelas , name-pl = tabelas , type = item , gender = m , Name-sg = Item , name-sg = item , Name-pl = Itens , name-pl = itens , type = footnote , gender = f , Name-sg = Nota , name-sg = nota , Name-pl = Notas , name-pl = notas , type = endnote , gender = f , Name-sg = Nota , name-sg = nota , Name-pl = Notas , name-pl = notas , type = note , gender = f , Name-sg = Nota , name-sg = nota , Name-pl = Notas , name-pl = notas , type = equation , gender = f , Name-sg = Equação , name-sg = equação , Name-pl = Equações , name-pl = equações , Name-sg-ab = Eq. , name-sg-ab = eq. , Name-pl-ab = Eqs. , name-pl-ab = eqs. , preref = {(} , postref = {)} , type = theorem , gender = m , Name-sg = Teorema , name-sg = teorema , Name-pl = Teoremas , name-pl = teoremas , type = lemma , gender = m , Name-sg = Lema , name-sg = lema , Name-pl = Lemas , name-pl = lemas , type = corollary , gender = m , Name-sg = Corolário , name-sg = corolário , Name-pl = Corolários , name-pl = corolários , type = proposition , gender = f , Name-sg = Proposição , name-sg = proposição , Name-pl = Proposições , name-pl = proposições , type = definition , gender = f , Name-sg = Definição , name-sg = definição , Name-pl = Definições , name-pl = definições , type = proof , gender = f , Name-sg = Demonstração , name-sg = demonstração , Name-pl = Demonstrações , name-pl = demonstrações , type = result , gender = m , Name-sg = Resultado , name-sg = resultado , Name-pl = Resultados , name-pl = resultados , type = remark , gender = f , Name-sg = Observação , name-sg = observação , Name-pl = Observações , name-pl = observações , type = example , gender = m , Name-sg = Exemplo , name-sg = exemplo , Name-pl = Exemplos , name-pl = exemplos , type = algorithm , gender = m , Name-sg = Algoritmo , name-sg = algoritmo , Name-pl = Algoritmos , name-pl = algoritmos , type = listing , gender = f , Name-sg = Listagem , name-sg = listagem , Name-pl = Listagens , name-pl = listagens , type = exercise , gender = m , Name-sg = Exercício , name-sg = exercício , Name-pl = Exercícios , name-pl = exercícios , type = solution , gender = f , Name-sg = Solução , name-sg = solução , Name-pl = Soluções , name-pl = soluções , % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % % \subsection{Spanish} % % Spanish language file has been initially provided by the author. % % \begin{macrocode} %<*package> \zcDeclareLanguage [ gender = { f , m } ] { spanish } % % \end{macrocode} % % \begin{macrocode} %<*lang-spanish> % \end{macrocode} % % \begin{macrocode} namesep = {\nobreakspace} , pairsep = {~y\nobreakspace} , listsep = {,~} , lastsep = {~y\nobreakspace} , tpairsep = {~y\nobreakspace} , tlistsep = {,~} , tlastsep = {~y\nobreakspace} , notesep = {~} , rangesep = {~a\nobreakspace} , type = book , gender = m , Name-sg = Libro , name-sg = libro , Name-pl = Libros , name-pl = libros , type = part , gender = f , Name-sg = Parte , name-sg = parte , Name-pl = Partes , name-pl = partes , type = chapter , gender = m , Name-sg = Capítulo , name-sg = capítulo , Name-pl = Capítulos , name-pl = capítulos , type = section , gender = f , Name-sg = Sección , name-sg = sección , Name-pl = Secciones , name-pl = secciones , type = paragraph , gender = m , Name-sg = Párrafo , name-sg = párrafo , Name-pl = Párrafos , name-pl = párrafos , type = appendix , gender = m , Name-sg = Apéndice , name-sg = apéndice , Name-pl = Apéndices , name-pl = apéndices , type = page , gender = f , Name-sg = Página , name-sg = página , Name-pl = Páginas , name-pl = páginas , rangesep = {\textendash} , type = line , gender = f , Name-sg = Línea , name-sg = línea , Name-pl = Líneas , name-pl = líneas , type = figure , gender = f , Name-sg = Figura , name-sg = figura , Name-pl = Figuras , name-pl = figuras , type = table , gender = m , Name-sg = Cuadro , name-sg = cuadro , Name-pl = Cuadros , name-pl = cuadros , type = item , gender = m , Name-sg = Punto , name-sg = punto , Name-pl = Puntos , name-pl = puntos , type = footnote , gender = f , Name-sg = Nota , name-sg = nota , Name-pl = Notas , name-pl = notas , type = endnote , gender = f , Name-sg = Nota , name-sg = nota , Name-pl = Notas , name-pl = notas , type = note , gender = f , Name-sg = Nota , name-sg = nota , Name-pl = Notas , name-pl = notas , type = equation , gender = f , Name-sg = Ecuación , name-sg = ecuación , Name-pl = Ecuaciones , name-pl = ecuaciones , preref = {(} , postref = {)} , type = theorem , gender = m , Name-sg = Teorema , name-sg = teorema , Name-pl = Teoremas , name-pl = teoremas , type = lemma , gender = m , Name-sg = Lema , name-sg = lema , Name-pl = Lemas , name-pl = lemas , type = corollary , gender = m , Name-sg = Corolario , name-sg = corolario , Name-pl = Corolarios , name-pl = corolarios , type = proposition , gender = f , Name-sg = Proposición , name-sg = proposición , Name-pl = Proposiciones , name-pl = proposiciones , type = definition , gender = f , Name-sg = Definición , name-sg = definición , Name-pl = Definiciones , name-pl = definiciones , type = proof , gender = f , Name-sg = Demostración , name-sg = demostración , Name-pl = Demostraciones , name-pl = demostraciones , type = result , gender = m , Name-sg = Resultado , name-sg = resultado , Name-pl = Resultados , name-pl = resultados , type = remark , gender = f , Name-sg = Observación , name-sg = observación , Name-pl = Observaciones , name-pl = observaciones , type = example , gender = m , Name-sg = Ejemplo , name-sg = ejemplo , Name-pl = Ejemplos , name-pl = ejemplos , type = algorithm , gender = m , Name-sg = Algoritmo , name-sg = algoritmo , Name-pl = Algoritmos , name-pl = algoritmos , type = listing , gender = m , Name-sg = Listado , name-sg = listado , Name-pl = Listados , name-pl = listados , type = exercise , gender = m , Name-sg = Ejercicio , name-sg = ejercicio , Name-pl = Ejercicios , name-pl = ejercicios , type = solution , gender = f , Name-sg = Solución , name-sg = solución , Name-pl = Soluciones , name-pl = soluciones , % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % % \subsection{Dutch} % % Dutch language file initially contributed by \texttt{niluxv} (\githubPR{5}). % All genders were checked against the ``Dikke Van Dale''. Many words have % multiple genders. % % \begin{macrocode} %<*package> \zcDeclareLanguage [ gender = { f , m , n } ] { dutch } % % \end{macrocode} % % \begin{macrocode} %<*lang-dutch> % \end{macrocode} % % \begin{macrocode} namesep = {\nobreakspace} , pairsep = {~en\nobreakspace} , listsep = {,~} , lastsep = {~en\nobreakspace} , tpairsep = {~en\nobreakspace} , tlistsep = {,~} , tlastsep = {,~en\nobreakspace} , notesep = {~} , rangesep = {~t/m\nobreakspace} , type = book , gender = n , Name-sg = Boek , name-sg = boek , Name-pl = Boeken , name-pl = boeken , type = part , gender = n , Name-sg = Deel , name-sg = deel , Name-pl = Delen , name-pl = delen , type = chapter , gender = n , Name-sg = Hoofdstuk , name-sg = hoofdstuk , Name-pl = Hoofdstukken , name-pl = hoofdstukken , type = section , gender = m , Name-sg = Paragraaf , name-sg = paragraaf , Name-pl = Paragrafen , name-pl = paragrafen , type = paragraph , gender = f , Name-sg = Alinea , name-sg = alinea , Name-pl = Alinea's , name-pl = alinea's , type = appendix , gender = { m , n } , Name-sg = Appendix , name-sg = appendix , Name-pl = Appendices , name-pl = appendices , type = page , gender = { f , m } , Name-sg = Pagina , name-sg = pagina , Name-pl = Pagina's , name-pl = pagina's , rangesep = {\textendash} , type = line , gender = m , Name-sg = Regel , name-sg = regel , Name-pl = Regels , name-pl = regels , type = figure , gender = { n , f , m } , Name-sg = Figuur , name-sg = figuur , Name-pl = Figuren , name-pl = figuren , type = table , gender = { f , m } , Name-sg = Tabel , name-sg = tabel , Name-pl = Tabellen , name-pl = tabellen , type = item , gender = n , Name-sg = Punt , name-sg = punt , Name-pl = Punten , name-pl = punten , type = footnote , gender = { f , m } , Name-sg = Voetnoot , name-sg = voetnoot , Name-pl = Voetnoten , name-pl = voetnoten , type = endnote , gender = { f , m } , Name-sg = Eindnoot , name-sg = eindnoot , Name-pl = Eindnoten , name-pl = eindnoten , type = note , gender = f , Name-sg = Opmerking , name-sg = opmerking , Name-pl = Opmerkingen , name-pl = opmerkingen , type = equation , gender = f , Name-sg = Vergelijking , name-sg = vergelijking , Name-pl = Vergelijkingen , name-pl = vergelijkingen , Name-sg-ab = Vgl. , name-sg-ab = vgl. , Name-pl-ab = Vgl.'s , name-pl-ab = vgl.'s , preref = {(} , postref = {)} , type = theorem , gender = f , Name-sg = Stelling , name-sg = stelling , Name-pl = Stellingen , name-pl = stellingen , % \end{macrocode} % % 2022-01-09, \texttt{niluxv}: An alternative plural is ``lemmata''. That is % also a correct English plural for lemma, but the English language file % chooses ``lemmas''. For consistency we therefore choose ``lemma's''. % % \begin{macrocode} type = lemma , gender = n , Name-sg = Lemma , name-sg = lemma , Name-pl = Lemma's , name-pl = lemma's , type = corollary , gender = n , Name-sg = Gevolg , name-sg = gevolg , Name-pl = Gevolgen , name-pl = gevolgen , type = proposition , gender = f , Name-sg = Propositie , name-sg = propositie , Name-pl = Proposities , name-pl = proposities , type = definition , gender = f , Name-sg = Definitie , name-sg = definitie , Name-pl = Definities , name-pl = definities , type = proof , gender = n , Name-sg = Bewijs , name-sg = bewijs , Name-pl = Bewijzen , name-pl = bewijzen , type = result , gender = n , Name-sg = Resultaat , name-sg = resultaat , Name-pl = Resultaten , name-pl = resultaten , type = remark , gender = f , Name-sg = Opmerking , name-sg = opmerking , Name-pl = Opmerkingen , name-pl = opmerkingen , type = example , gender = n , Name-sg = Voorbeeld , name-sg = voorbeeld , Name-pl = Voorbeelden , name-pl = voorbeelden , type = algorithm , gender = { n , f , m } , Name-sg = Algoritme , name-sg = algoritme , Name-pl = Algoritmes , name-pl = algoritmes , % \end{macrocode} % % 2022-01-09, \texttt{niluxv}: EN-NL Van Dale translates listing as (3) % ``uitdraai van computerprogramma'', ``listing''. % % \begin{macrocode} type = listing , gender = m , Name-sg = Listing , name-sg = listing , Name-pl = Listings , name-pl = listings , type = exercise , gender = { f , m } , Name-sg = Opgave , name-sg = opgave , Name-pl = Opgaven , name-pl = opgaven , type = solution , gender = f , Name-sg = Oplossing , name-sg = oplossing , Name-pl = Oplossingen , name-pl = oplossingen , % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % % \PrintIndex % %