diff options
author | Karl Berry <karl@freefriends.org> | 2021-11-24 21:31:19 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-11-24 21:31:19 +0000 |
commit | d1cb4d61e001c353e99cf0209daeb7e265e350c5 (patch) | |
tree | 5001e82104eca492dda3f8ffe60e281b668f1c20 /Master/texmf-dist/source/latex/zref-clever | |
parent | 2912d9e2b694d852f5d6812fe972ab65cf585b10 (diff) |
zref-clever (24nov21)
git-svn-id: svn://tug.org/texlive/trunk@61145 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/zref-clever')
-rw-r--r-- | Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx | 7161 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/zref-clever/zref-clever.ins | 99 |
2 files changed, 7260 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx b/Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx new file mode 100644 index 00000000000..024aa50a991 --- /dev/null +++ b/Master/texmf-dist/source/latex/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 +% +% diff --git a/Master/texmf-dist/source/latex/zref-clever/zref-clever.ins b/Master/texmf-dist/source/latex/zref-clever/zref-clever.ins new file mode 100644 index 00000000000..71259f4751f --- /dev/null +++ b/Master/texmf-dist/source/latex/zref-clever/zref-clever.ins @@ -0,0 +1,99 @@ +% \iffalse meta-comment +% +% File: zref-clever.ins +% +% 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 + +\input l3docstrip.tex + +\keepsilent + +\askforoverwritefalse + +\preamble + +This file was generated from file(s) 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. + +----------------------------------------------------------------------- + +\endpreamble + +\postamble +\endpostamble + +\generate{\file{zref-clever.sty}{\from{zref-clever.dtx}{package}}} +\generate{\file{zref-clever-english.dict}{\from{zref-clever.dtx}{dict-english}}} +\generate{\file{zref-clever-german.dict}{\from{zref-clever.dtx}{dict-german}}} +\generate{\file{zref-clever-french.dict}{\from{zref-clever.dtx}{dict-french}}} +\generate{\file{zref-clever-portuguese.dict}{\from{zref-clever.dtx}{dict-portuguese}}} +\generate{\file{zref-clever-spanish.dict}{\from{zref-clever.dtx}{dict-spanish}}} + +\endbatchfile |