summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/zref-clever/zref-clever.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/zref-clever/zref-clever.dtx')
-rw-r--r--macros/latex/contrib/zref-clever/zref-clever.dtx7161
1 files changed, 7161 insertions, 0 deletions
diff --git a/macros/latex/contrib/zref-clever/zref-clever.dtx b/macros/latex/contrib/zref-clever/zref-clever.dtx
new file mode 100644
index 0000000000..024aa50a99
--- /dev/null
+++ b/macros/latex/contrib/zref-clever/zref-clever.dtx
@@ -0,0 +1,7161 @@
+% \iffalse meta-comment
+%
+% File: zref-clever.dtx
+%
+% This file is part of the LaTeX package "zref-clever".
+%
+% Copyright (C) 2021 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 derived 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}
+%</driver>
+% \fi
+%
+% \DoNotIndex{\\,\{,\}}
+% \DoNotIndex{\c@,\cl@,\c@enumN,\p@}
+% \DoNotIndex{\the}
+%
+%
+% \NewDocumentCommand\opt{m}{\texttt{#1}}
+%
+% \pdfstringdefDisableCommands{%
+% \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{%
+% \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.%
+% }
+%
+%
+% \GetFileInfo{zref-clever.sty}
+%
+% \title{%
+% The \pkg{zref-clever} package implementation%
+% \thanks{This file describes \fileversion, released \filedate.}%
+% }
+%
+% \author{%
+% Gustavo Barros%
+% \thanks{\url{https://github.com/gusbrs/zref-clever}}%
+% }
+%
+% \date{\filedate}
+%
+% \maketitle
+%
+% \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
+% dictionaries (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} (see \url{https://tex.stackexchange.com/q/617905} and
+% \url{https://github.com/latex3/latex2e/pull/699}, thanks Phelype Oleinik).
+% Second, the support for \cs{@currentcounter} has been improved, including
+% \cs{footnote} and \pkg{amsmath} (see
+% \url{https://github.com/latex3/latex2e/issues/687}, thanks Frank Mittelbach
+% and Ulrike Fischer). 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} {2021-11-24} {0.1.0-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 `texdoc
+% source2e', section `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 (see also
+% \url{https://tex.stackexchange.com/q/611370/#comment1529282_611385}, thanks
+% Enrico Gregorio, aka `egreg').
+% \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 } { 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 } { dict-loaded }
+ { Loaded~'#1'~dictionary. }
+\msg_new:nnn { zref-clever } { dict-not-available }
+ { Dictionary~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~dictionary.~
+ 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. }
+% \end{macrocode}
+%
+%
+% \subsection{Data extraction}
+%
+%
+% \begin{macro}{\@@_def_extract: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{@@_def_extract:Nnnn} \Arg{tl val}
+% ~~\Arg{label} \Arg{prop} \Arg{default}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_def_extract:Nnnn #1#2#3#4
+ {
+ \exp_args:NNNo \exp_args:NNo \tl_set:Nn #1
+ { \zref@extractdefault {#2} {#3} {#4} }
+ }
+\cs_generate_variant:Nn \@@_def_extract: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_string:nN},
+% \cs{@@_get_ref_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_dict_prop}.
+%
+%
+% \begin{macro}
+% {
+% \l_@@_setup_type_tl ,
+% \l_@@_dict_language_tl ,
+% \l_@@_dict_decl_case_tl ,
+% \l_@@_dict_declension_seq ,
+% \l_@@_dict_gender_seq ,
+% }
+% Store ``current'' type, language, and declension cases in different places
+% for option and translation handling, notably in
+% \cs{@@_provide_dictionary:n}, \cs{zcRefTypeSetup}, and
+% \cs{zcLanguageSetup}. But also for translations retrieval, in
+% \cs{@@_get_type_transl:nnnN} and \cs{@@_get_default_transl:nnN}.
+% \begin{macrocode}
+\tl_new:N \l_@@_setup_type_tl
+\tl_new:N \l_@@_dict_language_tl
+\tl_new:N \l_@@_dict_decl_case_tl
+\seq_new:N \l_@@_dict_declension_seq
+\seq_new:N \l_@@_dict_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 ,
+ refpre ,
+ refpos ,
+ }
+% \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_string: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 ``dictionary 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 dictionary 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 ``dictionary 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 ``dictionary 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 dictionaries 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_@@_dict_ #2 _prop }
+ \tl_set:Nn \l_@@_dict_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}.
+% \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_@@_dict_ \l_@@_dict_language_tl _prop }
+ { declension } {#1}
+ } ,
+ declension .value_required:n = true ,
+ gender .code:n =
+ {
+ \prop_gput:cnn
+ { g_@@_dict_ \l_@@_dict_language_tl _prop }
+ { gender } {#1}
+ } ,
+ gender .value_required:n = true ,
+ allcaps .code:n =
+ {
+ \prop_gput:cnn
+ { g_@@_dict_ \l_@@_dict_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_@@_dict_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_@@_dict_declension_seq
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_language_tl _prop
+ }
+ { declension }
+ }
+ \seq_if_empty:NTF \l_@@_dict_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_@@_dict_declension_seq
+ \l_@@_ref_decl_case_tl
+ }
+ {
+ \seq_if_in:NVF \l_@@_dict_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_@@_dict_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_@@_dict_gender_seq
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_language_tl _prop
+ }
+ { gender }
+ }
+ \seq_if_empty:NTF \l_@@_dict_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_@@_dict_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 \cs{l_@@_capitalize_bool} is set to \texttt{true} when the language
+% was declared with \opt{allcaps} option.
+% \begin{macrocode}
+ \str_if_eq:eeT
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_language_tl _prop
+ }
+ { allcaps }
+ }
+ { true }
+ { \bool_set_true:N \l_@@_capitalize_bool }
+ }
+ {
+% \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{Dictionaries}
+%
+% Contrary to general options and type options, which are always \emph{local},
+% ``dictionaries'', ``translations'' or ``language-specific settings'' are
+% always \emph{global}. Hence, the loading of built-in dictionaries, as well
+% as settings done with \cs{zcLanguageSetup}, should set the relevant
+% variables globally.
+%
+% The built-in dictionaries and their related infrastructure are designed to
+% perform ``on the fly'' loading of dictionaries, ``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 dictionary
+% 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 dictionaries 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
+% dictionary 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/dictionary\}} by
+% \cs{@@_provide_dictionary:n}. And they use the same syntax and options as
+% \cs{zcLanguageSetup} does. The dictionary file itself is read with
+% \cs{ExplSyntaxOn} with the usual implications for white-space and catcodes.
+%
+% \cs{@@_provide_dictionary:n} is only meant to load the built-in
+% dictionaries. 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_@@_dict_\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_dictionaries_seq}
+% Used to keep track of whether a dictionary has already been loaded or not.
+% \begin{macrocode}
+\seq_new:N \g_@@_loaded_dictionaries_seq
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\l_@@_load_dict_verbose_bool}
+% Controls whether \cs{@@_provide_dictionary:n} fails silently or verbosely
+% in case of unknown languages or dictionaries not found.
+% \begin{macrocode}
+\bool_new:N \l_@@_load_dict_verbose_bool
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\@@_provide_dictionary:n}
+% Load dictionary for known \meta{language} if it is available and if it has
+% not already been loaded.
+% \begin{syntax}
+% \cs{@@_provide_dictionary:n} \Arg{language}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_provide_dictionary:n #1
+ {
+ \group_begin:
+ \@bsphack
+ \prop_get:NnNTF \g_@@_languages_prop {#1}
+ \l_@@_dict_language_tl
+ {
+ \seq_if_in:NVF
+ \g_@@_loaded_dictionaries_seq
+ \l_@@_dict_language_tl
+ {
+ \exp_args:Nx \file_get:nnNTF
+ { zref-clever- \l_@@_dict_language_tl .dict }
+ { \ExplSyntaxOn }
+ \l_tmpa_tl
+ {
+ \tl_clear:N \l_@@_setup_type_tl
+ \exp_args:NNx \seq_set_from_clist:Nn
+ \l_@@_dict_declension_seq
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_language_tl _prop
+ }
+ { declension }
+ }
+ \seq_if_empty:NTF \l_@@_dict_declension_seq
+ { \tl_clear:N \l_@@_dict_decl_case_tl }
+ {
+ \seq_get_left:NN \l_@@_dict_declension_seq
+ \l_@@_dict_decl_case_tl
+ }
+ \exp_args:NNx \seq_set_from_clist:Nn
+ \l_@@_dict_gender_seq
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_language_tl _prop
+ }
+ { gender }
+ }
+ \keys_set:nV { zref-clever / dictionary } \l_tmpa_tl
+ \seq_gput_right:NV \g_@@_loaded_dictionaries_seq
+ \l_@@_dict_language_tl
+ \msg_note:nnx { zref-clever } { dict-loaded }
+ { \l_@@_dict_language_tl }
+ }
+ {
+ \bool_if:NT \l_@@_load_dict_verbose_bool
+ {
+ \msg_warning:nnx { zref-clever } { dict-not-available }
+ { \l_@@_dict_language_tl }
+ }
+% \end{macrocode}
+% Even if we don't have the actual dictionary, 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
+% dictionary 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 translations themselves, by means of \cs{zcLanguageSetup},
+% everything would be in place, and they could use the \opt{lang} option
+% multiple times, and the \texttt{dict-not-available} warning would never go
+% away.
+% \begin{macrocode}
+ \seq_gput_right:NV \g_@@_loaded_dictionaries_seq
+ \l_@@_dict_language_tl
+ }
+ }
+ }
+ {
+ \bool_if:NT \l_@@_load_dict_verbose_bool
+ { \msg_warning:nnn { zref-clever } { unknown-language-load } {#1} }
+ }
+ \@esphack
+ \group_end:
+ }
+\cs_generate_variant:Nn \@@_provide_dictionary:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\@@_provide_dictionary_verbose:n}
+% Does the same as \cs{@@_provide_dictionary:n}, but warns if the loading of
+% the dictionary has failed.
+% \begin{syntax}
+% \cs{@@_provide_dictionary_verbose:n} \Arg{language}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_provide_dictionary_verbose:n #1
+ {
+ \group_begin:
+ \bool_set_true:N \l_@@_load_dict_verbose_bool
+ \@@_provide_dictionary:n {#1}
+ \group_end:
+ }
+\cs_generate_variant:Nn \@@_provide_dictionary_verbose:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}
+% {
+% \@@_provide_dict_type_transl:nn ,
+% \@@_provide_dict_default_transl:nn ,
+% }
+% A couple of auxiliary functions for the of
+% \texttt{{zref-clever/dictionary}} keys set in
+% \cs{@@_provide_dictionary:n}. They respectively ``provide'' (i.e. set if
+% it value does not exist, do nothing if it already does) ``type-specific''
+% and ``default'' translations. Both receive \meta{key} and
+% \meta{translation} as arguments, but \cs{@@_provide_dict_type_transl:nn}
+% relies on the current value of \cs{l_@@_setup_type_tl}, as set by the
+% \texttt{type} key.
+% \begin{syntax}
+% \cs{@@_provide_dict_type_transl:nn} \Arg{key} \Arg{translation}
+% \cs{@@_provide_dict_default_transl:nn} \Arg{key} \Arg{translation}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_provide_dict_type_transl:nn #1#2
+ {
+ \exp_args:Nnx \prop_gput_if_new:cnn
+ { g_@@_dict_ \l_@@_dict_language_tl _prop }
+ { type- \l_@@_setup_type_tl - #1 } {#2}
+ }
+\cs_new_protected:Npn \@@_provide_dict_default_transl:nn #1#2
+ {
+ \prop_gput_if_new:cnn
+ { g_@@_dict_ \l_@@_dict_language_tl _prop }
+ { default- #1 } {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% The set of keys for \texttt{{zref-clever/dictionary}}, which is used to
+% process the dictionary files in \cs{@@_provide_dictionary:n}. The no-op
+% cases for each category have their messages sent to ``info''. These
+% messages should not occur, as long as the dictionaries are well formed, but
+% they're placed there nevertheless, and can be leveraged in regression tests.
+%
+% \begin{macrocode}
+\keys_define:nn { zref-clever / dictionary }
+ {
+ 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_@@_dict_declension_seq
+ {
+ \msg_info:nnxx { zref-clever } { language-no-decl-setup }
+ { \l_@@_dict_language_tl } {#1}
+ }
+ {
+ \seq_if_in:NnTF \l_@@_dict_declension_seq {#1}
+ { \tl_set:Nn \l_@@_dict_decl_case_tl {#1} }
+ {
+ \msg_info:nnxx { zref-clever } { unknown-decl-case }
+ {#1} { \l_@@_dict_language_tl }
+ \seq_get_left:NN \l_@@_dict_declension_seq
+ \l_@@_dict_decl_case_tl
+ }
+ }
+ } ,
+ case .value_required:n = true ,
+ gender .code:n =
+ {
+ \seq_if_empty:NTF \l_@@_dict_gender_seq
+ {
+ \msg_info:nnxxx { zref-clever } { language-no-gender }
+ { \l_@@_dict_language_tl } { gender } {#1}
+ }
+ {
+ \tl_if_empty:NTF \l_@@_setup_type_tl
+ {
+ \msg_info:nnn { zref-clever }
+ { option-only-type-specific } { gender }
+ }
+ {
+ \seq_if_in:NnTF \l_@@_dict_gender_seq {#1}
+ { \@@_provide_dict_type_transl:nn { gender } {#1} }
+ {
+ \msg_info:nnxx { zref-clever } { gender-not-declared }
+ { \l_@@_dict_language_tl } {#1}
+ }
+ }
+ }
+ } ,
+ gender .value_required:n = true ,
+ }
+\seq_map_inline:Nn
+ \c_@@_ref_options_necessarily_not_type_specific_seq
+ {
+ \keys_define:nn { zref-clever / dictionary }
+ {
+ #1 .value_required:n = true ,
+ #1 .code:n =
+ {
+ \tl_if_empty:NTF \l_@@_setup_type_tl
+ { \@@_provide_dict_default_transl: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 / dictionary }
+ {
+ #1 .value_required:n = true ,
+ #1 .code:n =
+ {
+ \tl_if_empty:NTF \l_@@_setup_type_tl
+ { \@@_provide_dict_default_transl:nn {#1} {##1} }
+ { \@@_provide_dict_type_transl:nn {#1} {##1} }
+ } ,
+ }
+ }
+\seq_map_inline:Nn
+ \c_@@_ref_options_type_names_seq
+ {
+ \keys_define:nn { zref-clever / dictionary }
+ {
+ #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_@@_dict_decl_case_tl
+ { \@@_provide_dict_type_transl:nn {#1} {##1} }
+ {
+ \@@_provide_dict_type_transl:nn
+ { \l_@@_dict_decl_case_tl - #1 } {##1}
+ }
+ }
+ } ,
+ }
+ }
+% \end{macrocode}
+%
+%
+%
+% \subsubsection*{Fallback}
+%
+% All ``strings'' queried with \cs{@@_get_ref_string: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 translation, 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_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_dict_prop
+\prop_gset_from_keyval:Nn \g_@@_fallback_dict_prop
+ {
+ tpairsep = {,~} ,
+ tlistsep = {,~} ,
+ tlastsep = {,~} ,
+ notesep = {~} ,
+ namesep = {\nobreakspace} ,
+ pairsep = {,~} ,
+ listsep = {,~} ,
+ lastsep = {,~} ,
+ rangesep = {\textendash} ,
+ refpre = {} ,
+ refpos = {} ,
+ }
+% \end{macrocode}
+%
+%
+%
+% \subsubsection*{Get translations}
+%
+% \begin{macro}{\@@_get_type_transl:nnnNF}
+% Get type-specific translation 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_type_transl:nnnNF} \Arg{language} \Arg{type} \Arg{key}
+% ~~\meta{tl variable} \Arg{false code}
+% \end{syntax}
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn
+ \@@_get_type_transl:nnnN #1#2#3#4 { F }
+ {
+ \prop_get:NnNTF \g_@@_languages_prop {#1}
+ \l_@@_dict_language_tl
+ {
+ \prop_get:cnNTF
+ { g_@@_dict_ \l_@@_dict_language_tl _prop }
+ { type- #2 - #3 } #4
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn
+ \@@_get_type_transl:nnnN { xxxN , xxnN } { F }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\@@_get_default_transl:nnNF}
+% Get default translation 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_default_transl:nnNF} \Arg{language} \Arg{key}
+% ~~\meta{tl variable} \Arg{false code}
+% \end{syntax}
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn
+ \@@_get_default_transl:nnN #1#2#3 { F }
+ {
+ \prop_get:NnNTF \g_@@_languages_prop {#1}
+ \l_@@_dict_language_tl
+ {
+ \prop_get:cnNTF
+ { g_@@_dict_ \l_@@_dict_language_tl _prop }
+ { default- #2 } #3
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn
+ \@@_get_default_transl:nnN { xnN } { F }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\@@_get_fallback_transl:nNF}
+% Get fallback translation 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_transl:nNF} \Arg{key}
+% ~~\meta{tl variable} \Arg{false code}
+% \end{syntax}
+% \begin{macrocode}
+% {<key>}<tl var to set>
+\prg_new_protected_conditional:Npnn
+ \@@_get_fallback_transl:nN #1#2 { F }
+ {
+ \prop_get:NnNTF \g_@@_fallback_dict_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 (see \url{https://github.com/ho-tex/zref/issues/13},
+% thanks Ulrike Fischer). 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_bool
+\bool_new:N \l_@@_capitalize_first_bool
+\keys_define:nn { zref-clever / reference }
+ {
+ cap .bool_set:N = \l_@@_capitalize_bool ,
+ cap .initial:n = false ,
+ 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_@@_abbrev_bool
+\bool_new:N \l_@@_noabbrev_first_bool
+\keys_define:nn { zref-clever / reference }
+ {
+ abbrev .bool_set:N = \l_@@_abbrev_bool ,
+ abbrev .initial:n = false ,
+ 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 dictionary
+% 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 dictionary loading
+% is done verbosely, from when we are setting the default value (here), in
+% which case the dictionary 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_dictionary_verbose:x
+ { \l_@@_ref_language_tl }
+ }
+
+ { main }
+ {
+ \tl_set:Nn \l_@@_ref_language_tl
+ { \l_@@_main_language_tl }
+ \@@_provide_dictionary_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_dictionary_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_dictionary:x { \l_@@_ref_language_tl }
+% \end{macrocode}
+% Redefinition of the \texttt{lang} key option for the document body. Also,
+% drop the verbose dictionary 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_dictionary:x
+ { \l_@@_ref_language_tl }
+ }
+
+ { main }
+ {
+ \tl_set:Nn \l_@@_ref_language_tl
+ { \l_@@_main_language_tl }
+ \@@_provide_dictionary: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_dictionary: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.
+%
+% Thanks \texttt{@samcarter} and Alan Munn for useful comments about
+% declension on the TeX.SX chat. Also, 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}
+% (see \url{https://tex.stackexchange.com/a/618439}, thanks David Carlisle).
+%
+% \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}
+%
+%
+% \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_string:nN} and
+% \cs{@@_get_ref_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} }
+ } ,
+ }
+ }
+% \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 translation, hence they override any language-specific
+% setting, either done at \cs{zcLanguageSetup} or by the package's
+% dictionaries. 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
+% translations, regardless of their emptiness. In principle, we could live
+% with the situation of, once a setting has been made in
+% \cs{l_@@_type_<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 translations 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 and some discussion about it, see
+% \url{https://tex.stackexchange.com/q/614690} (thanks Jonathan P.\ Spratte,
+% aka `Skillmon', and Phelype Oleinik) and
+% \url{https://github.com/latex3/latex3/pull/988}.
+%
+% \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}
+ }
+ } ,
+ }
+ }
+% \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) translations. When the
+% \texttt{type} key is given with a value, the options following it will set
+% ``type-specific'' translations 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_@@_dict_language_tl
+ {
+ \tl_clear:N \l_@@_setup_type_tl
+ \exp_args:NNx \seq_set_from_clist:Nn
+ \l_@@_dict_declension_seq
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_language_tl _prop
+ }
+ { declension }
+ }
+ \seq_if_empty:NTF \l_@@_dict_declension_seq
+ { \tl_clear:N \l_@@_dict_decl_case_tl }
+ {
+ \seq_get_left:NN \l_@@_dict_declension_seq
+ \l_@@_dict_decl_case_tl
+ }
+ \exp_args:NNx \seq_set_from_clist:Nn
+ \l_@@_dict_gender_seq
+ {
+ \prop_item:cn
+ {
+ g_@@_dict_
+ \l_@@_dict_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_type_transl:nnnn ,
+% \@@_declare_default_transl:nnn ,
+% }
+% A couple of auxiliary functions for the of
+% \texttt{{zref-clever/translation}} keys set in \cs{zcLanguageSetup}. They
+% respectively declare (unconditionally set) ``type-specific'' and
+% ``default'' translations.
+% \begin{syntax}
+% \cs{@@_declare_type_transl:nnnn} \Arg{language} \Arg{type}
+% ~~\Arg{key} \Arg{translation}
+% \cs{@@_declare_default_transl:nnn} \Arg{language}
+% ~~\Arg{key} \Arg{translation}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_declare_type_transl:nnnn #1#2#3#4
+ {
+ \prop_gput:cnn { g_@@_dict_ #1 _prop }
+ { type- #2 - #3 } {#4}
+ }
+\cs_generate_variant:Nn \@@_declare_type_transl:nnnn { VVnn , VVxn }
+\cs_new_protected:Npn \@@_declare_default_transl:nnn #1#2#3
+ {
+ \prop_gput:cnn { g_@@_dict_ #1 _prop }
+ { default- #2 } {#3}
+ }
+\cs_generate_variant:Nn \@@_declare_default_transl:nnn { Vnn }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% The set of keys for \texttt{{zref-clever/langsetup}}, which is used to
+% set language-specific translations 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_@@_dict_declension_seq
+ {
+ \msg_warning:nnxx { zref-clever } { language-no-decl-setup }
+ { \l_@@_dict_language_tl } {#1}
+ }
+ {
+ \seq_if_in:NnTF \l_@@_dict_declension_seq {#1}
+ { \tl_set:Nn \l_@@_dict_decl_case_tl {#1} }
+ {
+ \msg_warning:nnxx { zref-clever } { unknown-decl-case }
+ {#1} { \l_@@_dict_language_tl }
+ \seq_get_left:NN \l_@@_dict_declension_seq
+ \l_@@_dict_decl_case_tl
+ }
+ }
+ } ,
+ case .value_required:n = true ,
+ gender .code:n =
+ {
+ \seq_if_empty:NTF \l_@@_dict_gender_seq
+ {
+ \msg_warning:nnxxx { zref-clever } { language-no-gender }
+ { \l_@@_dict_language_tl } { gender } {#1}
+ }
+ {
+ \tl_if_empty:NTF \l_@@_setup_type_tl
+ {
+ \msg_warning:nnn { zref-clever }
+ { option-only-type-specific } { gender }
+ }
+ {
+ \seq_if_in:NnTF \l_@@_dict_gender_seq {#1}
+ {
+ \@@_declare_type_transl:VVnn
+ \l_@@_dict_language_tl
+ \l_@@_setup_type_tl
+ { gender } {#1}
+ }
+ {
+ \msg_warning:nnxx { zref-clever } { gender-not-declared }
+ { \l_@@_dict_language_tl } {#1}
+ }
+ }
+ }
+ } ,
+ gender .value_required: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_default_transl:Vnn
+ \l_@@_dict_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_default_transl:Vnn
+ \l_@@_dict_language_tl
+ {#1} {##1}
+ }
+ {
+ \@@_declare_type_transl:VVnn
+ \l_@@_dict_language_tl
+ \l_@@_setup_type_tl
+ {#1} {##1}
+ }
+ } ,
+ }
+ }
+\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_@@_dict_decl_case_tl
+ {
+ \@@_declare_type_transl:VVnn
+ \l_@@_dict_language_tl
+ \l_@@_setup_type_tl
+ {#1} {##1}
+ }
+ {
+ \@@_declare_type_transl:VVxn
+ \l_@@_dict_language_tl
+ \l_@@_setup_type_tl
+ { \l_@@_dict_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 dictionary 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_dictionary:x} does nothing if the dictionary is
+% already loaded.
+% \begin{macrocode}
+ \@@_provide_dictionary: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_string: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
+ {
+ \@@_def_extract: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
+ {
+ \@@_def_extract:Nnnn
+ \l_@@_label_type_a_tl {#1} { zc@type } { zc@missingtype }
+ \@@_def_extract: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
+ {
+ \@@_def_extract:Nnnn \l_@@_label_enclval_a_tl
+ {#1} { zc@enclval } { \c_empty_tl }
+ \tl_reverse:N \l_@@_label_enclval_a_tl
+ \@@_def_extract:Nnnn \l_@@_label_enclval_b_tl
+ {#2} { zc@enclval } { \c_empty_tl }
+ \tl_reverse:N \l_@@_label_enclval_b_tl
+ \@@_def_extract:Nnnn \l_@@_label_extdoc_a_tl
+ {#1} { externaldocument } { \c_empty_tl }
+ \@@_def_extract: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
+% (and good ones at that) see \url{https://tex.stackexchange.com/q/611370}
+% (thanks Enrico Gregorio, Phelype Oleinik, and Steven B.\ Segletes). 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_@@_refpre_tl ,
+% \l_@@_refpos_tl ,
+% \l_@@_namefont_tl ,
+% \l_@@_reffont_tl ,
+% }
+% Auxiliary variables for \cs{@@_typeset_refs:} separators, refpre/pos and
+% font 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_@@_refpre_tl
+\tl_new:N \l_@@_refpos_tl
+\tl_new:N \l_@@_namefont_tl
+\tl_new:N \l_@@_reffont_tl
+% \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_string:nN { tpairsep }
+ \l_@@_tpairsep_tl
+ \@@_get_ref_string:nN { tlistsep }
+ \l_@@_tlistsep_tl
+ \@@_get_ref_string: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 }
+ }
+ {
+ \@@_def_extract:NVnn \l_@@_label_type_a_tl
+ \l_@@_label_a_tl { zc@type } { zc@missingtype }
+ \@@_def_extract: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, refpre/pos and font options, once per
+ % type.
+ \int_compare:nNnT { \l_@@_label_count_int } = { 0 }
+ {
+ \@@_get_ref_string:nN { namesep }
+ \l_@@_namesep_tl
+ \@@_get_ref_string:nN { pairsep }
+ \l_@@_pairsep_tl
+ \@@_get_ref_string:nN { listsep }
+ \l_@@_listsep_tl
+ \@@_get_ref_string:nN { lastsep }
+ \l_@@_lastsep_tl
+ \@@_get_ref_string:nN { rangesep }
+ \l_@@_rangesep_tl
+ \@@_get_ref_string:nN { refpre }
+ \l_@@_refpre_tl
+ \@@_get_ref_string:nN { refpos }
+ \l_@@_refpos_tl
+ \@@_get_ref_font:nN { namefont }
+ \l_@@_namefont_tl
+ \@@_get_ref_font:nN { reffont }
+ \l_@@_reffont_tl
+ }
+
+ % 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_@@_refpre_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_@@_refpre_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_@@_refpos_tl }
+ }
+ \bool_if:NF \l_@@_preposinlink_bool
+ { \exp_not:V \l_@@_refpos_tl }
+ }
+ {
+ \exp_not:V \l_@@_refpre_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_@@_refpos_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_@@_refpre_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_@@_refpos_tl }
+ }
+ \bool_if:NF \l_@@_preposinlink_bool
+ { \exp_not:V \l_@@_refpos_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_@@_refpre_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_@@_refpre_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_@@_refpos_tl }
+ }
+ \bool_if:NF \l_@@_preposinlink_bool
+ { \exp_not:V \l_@@_refpos_tl }
+ }
+ {
+ \exp_not:V \l_@@_refpre_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_@@_refpos_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_type_transl: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 }
+ \tl_if_eq:NNF
+ \l_@@_ref_gender_tl
+ \l_@@_type_name_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_type_transl: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_type_transl:xxxNF
+ { \l_@@_ref_language_tl }
+ { \l_@@_type_first_label_type_tl }
+ { \l_@@_name_format_tl }
+ \l_@@_type_name_tl
+ {
+ \@@_get_type_transl: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
+ {
+ \@@_def_extract:Nnnn \l_@@_label_extdoc_a_tl
+ {#1} { externaldocument } { \c_empty_tl }
+ \@@_def_extract: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, a couple of functions for retrieving options values, according to
+% the relevant precedence rules. They both receive an \meta{option} as
+% argument, and store the retrieved value in \meta{tl variable}. 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
+% \cs{@@_get_ref_string:nN} and \cs{@@_get_ref_font:nN} is the kind of option
+% each should be used for. \cs{@@_get_ref_string:nN} is meant for the general
+% options, and attempts to find values for them in all precedence levels (four
+% plus ``fallback''). \cs{@@_get_ref_font:nN} is intended for ``font''
+% options, which cannot be ``language-specific'', thus for these we just
+% search general options and type options.
+%
+% \begin{macro}{\@@_get_ref_string:nN}
+% \begin{syntax}
+% \cs{@@_get_ref_string:nN} \Arg{option} \Arg{tl variable}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_ref_string: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 specific translations.
+ \@@_get_type_transl:xxnNF
+ { \l_@@_ref_language_tl }
+ { \l_@@_label_type_a_tl }
+ {#1} #2
+ {
+ % If not found, try default translations.
+ \@@_get_default_transl:xnNF
+ { \l_@@_ref_language_tl }
+ {#1} #2
+ {
+ % If not found, try fallback.
+ \@@_get_fallback_transl:nNF {#1} #2
+ {
+ \tl_clear:N #2
+ \msg_warning:nnn { zref-clever }
+ { missing-string } {#1}
+ }
+ }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_get_ref_font:nN}
+% \begin{syntax}
+% \cs{@@_get_ref_font:nN} \Arg{option} \Arg{tl variable}
+% \end{syntax}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_get_ref_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}
+%
+%
+%
+% \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},
+% thanks Phelype Oleinik). The 2021-11-15 kernel release already handle this
+% gracefully (see \url{https://github.com/latex3/latex2e/pull/699}, thanks
+% Phelype Oleinik).
+%
+%
+% \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 ,
+ refpre = {\textup{(}} ,
+ refpos = {\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}
+%</package>
+% \end{macrocode}
+%
+%
+%
+% \section{Dictionaries}
+%
+% \subsection{English}
+%
+% \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 }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*dict-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 = subappendix ,
+ 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 ,
+ name-sg-ab = p. ,
+ name-pl-ab = pp. ,
+ 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. ,
+ refpre = {(} ,
+ refpos = {)} ,
+
+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}
+%</dict-english>
+% \end{macrocode}
+%
+%
+%
+% \subsection{German}
+%
+% \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 }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*dict-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 = subappendix ,
+ 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 ,
+ refpre = {(} ,
+ refpos = {)} ,
+
+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}
+%</dict-german>
+% \end{macrocode}
+%
+%
+%
+% \subsection{French}
+%
+% \begin{macrocode}
+%<*package>
+\zcDeclareLanguage [ gender = { f , m } ] { french }
+\zcDeclareLanguageAlias { acadian } { french }
+\zcDeclareLanguageAlias { canadien } { french }
+\zcDeclareLanguageAlias { francais } { french }
+\zcDeclareLanguageAlias { frenchb } { french }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*dict-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 = subappendix ,
+ 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 = {\textendash} ,
+
+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 ,
+ refpre = {(} ,
+ refpos = {)} ,
+
+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 = f ,
+ Name-sg = Liste ,
+ name-sg = liste ,
+ Name-pl = Listes ,
+ name-pl = listes ,
+
+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}
+%</dict-french>
+% \end{macrocode}
+%
+%
+%
+% \subsection{Portuguese}
+%
+% \begin{macrocode}
+%<*package>
+\zcDeclareLanguage [ gender = { f , m } ] { portuguese }
+\zcDeclareLanguageAlias { brazilian } { portuguese }
+\zcDeclareLanguageAlias { brazil } { portuguese }
+\zcDeclareLanguageAlias { portuges } { portuguese }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*dict-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 = subappendix ,
+ 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 ,
+ name-sg-ab = p. ,
+ name-pl-ab = pp. ,
+ 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. ,
+ refpre = {(} ,
+ refpos = {)} ,
+
+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}
+%</dict-portuguese>
+% \end{macrocode}
+%
+%
+%
+% \subsection{Spanish}
+%
+% \begin{macrocode}
+%<*package>
+\zcDeclareLanguage [ gender = { f , m } ] { spanish }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*dict-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 = subappendix ,
+ 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 ,
+ refpre = {(} ,
+ refpos = {)} ,
+
+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}
+%</dict-spanish>
+% \end{macrocode}
+%
+%
+% \PrintIndex
+%
+%