3

(This question and answer led to the new package cleveref-usedon, see the CTAN version or the dev version on github.)

I am trying to overload \label and \cref, so that the following happens (similar in spirit to backref's \backcite):

  • In some preliminary chapter, I define in Theorem A a label \label{thm:A}.
  • Later in the main text I am referencing Theorem A via \cref{thm:A}.
  • Retrospectively, I would like the reader of the prelim chapter to know where the result will be used later on in the main text by letting the header of Theorem A be followed by a "Used on page XYZ." phrase.

I have tried the following which works when the label is only referenced once. Basically, I create a command \crefusedon which in turn defines a label which adds a prefix backref: to the original labelname, e.g. backref:thm:A. The label command is overloaded to search for the existence of backref:LABELNAME and print the text above. The \cref command is overloaded to include \crefusedon. This way I only have to manually add the option [usedon] to all the \cref's in my main text which need it and the rest work as before.

Problem(s):

  • The downside of this is that it currently only works for one (or rather the last) call of \crefusedon. Of course, it would be much better to actually take note of all \crefusedon calls and have a phrase such as "Used on pages X, Y and Z." if there multiple calls. Can I use backref for this?
  • I can \RenewDocumentCommand \label but for some reason not \cref. Any idea why?
  • Somewhat confusingly the hyperlink in the \cpageref does not link to the correct page in this MWE. However, in my normal document the \cpageref hyperlink works but sometimes the opposite \crefusedon hyperlink points to a (very) wrong page.

I'm guessing I can somehow use or modify \backcite to do what I want but I've had trouble understanding backref's and cleveref's codebase. What's a sensible solution to this? If backref can do this, I'm happy to scrap the below half-solution. Btw, I am using biblatex in my normal document and know that there might be a problem with backref in that case.

\documentclass{amsart}
\usepackage{amsthm}             %AMS theorems
\usepackage{thmtools}           %better theorem editing interface
\declaretheorem[name=Theorem]{theorem}

\usepackage{tikz} \usepackage{etoolbox} \usepackage{xparse}

\usepackage{hyperref} %========================================= % -------- Exceptions to Hyperref (load last) ---------- %========================================= \usepackage[capitalise]{cleveref} % needs to be loaded AFTER hyperref % to not conflict with hyperref's \autoref

%========================================= % -------- BEGIN Overload label and cref ---------- %========================================= \makeatletter \NewDocumentCommand{\usedon}{ m }{% % Check if the label backref:<LABELNAME> exists \ifcsdef{r@backref:#1}{% \emph{(Used on \cpageref{backref:#1}.)}% \% }{} }% \makeatother \AtBeginDocument{ \let\origlabel\label \let\origcref\cref \RenewDocumentCommand{\label}{ m }{% \origlabel{#1}\usedon{#1}% }% \NewDocumentCommand{\crefusedon}{ s o m }{% \IfBooleanTF{#1}{%
\origcref*{#3} }{% \origcref{#3} }% \ifstrequal{#2}{usedon}{% % \cref can also get a list of labels as input, loop through them \foreach \x in {#3}{ \label{backref:\x}% }% }{}% }% \let\cref\crefusedon }% %========================================= % -------- END Overload label and cref ---------- %=========================================

\begin{document} \section{Introduction to wonders} \begin{theorem} \label{thm:egregium} This is a great theorem about wonderful mathematics. \end{theorem}

\begin{theorem} \label{thm:inconspicuam} A plane is flat. \end{theorem}

\clearpage As seen in \cref[usedon]{thm:egregium} the earth is positively curved and according to \cref{thm:inconspicuam} flat earth is not.

\clearpage As seen in \cref[usedon]{thm:egregium} the earth is positively curved and according to \cref{thm:inconspicuam} flat earth is not.

\end{document}

Sven Pistre
  • 171
  • 7

1 Answers1

3

(This question and answer led to the new package cleveref-usedon, see the CTAN version or the dev version on github.)

I'm not sure this is a useful feature for anyone but me and potentially using a package such as \zref might be better suited. In any case, this has prompted me to learn and implement a solution with expl3. Comments on anything wrong with my expl3 code are welcome. Run pdflatex twice to see the result. I've used the bugfix from here to repair cleveref's problem for ranges when calling \cpageref with multiple arguments.

The commands \cref, \Cref and \label retain their normal behaviour but the \cref commands have an optional extra argument UsedOn. When referencing a label via \cref[UsedOn]{LABELNAME} the following happens:

  • \AtBeginDocument and \ReadFromAux: We read from the .aux file (see \WriteToAux) a key-value list, stored in the global proplist \g_UsedOn_kv_prop. For each key (LABELNAME) we create and set a counter LastRun@LABELNAME to the value (max calls from last run).
  • \cref and \Cref: They call the original cleveref version and then call the custom \crefUsedOnProcessor.
  • \crefUsedOnProcessor: If the optional argument UsedOn was passed to \cref then we loop through the LABELNAMEs passed to \cref (which can receive a list of labelnames). For each LABELNAME we increase (or create and increase) the counter ThisRun@UsedOn@LABELNAME. If need be, update and store the counter LastRun@UsedOn@LABELNAME in \g_UsedOn_kv_prop. Then we create a new label UsedOn@LABELNAME@NUMBER with the original \label command where NUMBER is the current value of ThisRun@UsedOn@LABELNAME from the current run.
  • \label: Calls the original cleveref version and then calls the custom \PrintUsedOnLabel.
  • \PrintUsedOnLabel: If LABELNAME has been references with UsedOn option at least once then the refence UsedOn@LABELNAME@1 exists. In this case we create a clist of parameters we want to pass to \cpageref to create our Used on page... phrase. We need UsedOn@LABELNAME@1 to UsedOn@LABELNAME@MAX where MAX is the value of LastRun@UsedOn@LABELNAME.
  • \AtEndDocument and \WriteToAux: We store the global proplist \g_UsedOn_kv_prop in the .aux file.
\documentclass{article}
\usepackage{amsthm}             %AMS theorems
\usepackage{mathtools}          %AMSmath++ facilities
\usepackage{thmtools}           %better theorem editing interface

\usepackage{xparse}

\usepackage{hyperref} %========================================= % -------- Exceptions to Hyperref (load last) ---------- %========================================= \usepackage[capitalise]{cleveref} % needs to be loaded AFTER hyperref % because both update \label at the hook % \AtBeginDocument and we want to use % cleveref's \label \crefname{page}{page}{pages} % do NOT capitalise pages for more visual appeal % This fixes the range bug for \cpageref in cleveref 0.21.4 % See https://tex.stackexchange.com/q/603514/267438 \makeatletter \newcommand{@setcpagerefrange}[3]{% @@setcpagerefrange{#1}{#2}{cref}{#3}} \newcommand{@setCpagerefrange}[3]{% @@setcpagerefrange{#1}{#2}{Cref}{#3}} \newcommand*{@setlabelcpagerefrange}[3]{% @@setcpagerefrange{#1}{#2}{labelcref}{#3}} \makeatother

% declaretheorem needs to be executed AFTER loading cleveref % with the additional refname and Refname options % see https://tex.stackexchange.com/a/498242/267438 % and the thmtools documentation \declaretheorem[numberwithin=section, name=Theorem, refname={Theorem,Theorems}, Refname={Theorem,Theorems}]{theorem} \declaretheorem[sibling=theorem, name=Lemma, refname={Lemma,Lemmas}, Refname={Lemma,Lemmas}]{lemma}

\usepackage{showlabels} % to see the labels in the margin

%========================================= % -------- BEGIN Overload label and cref ---------- %========================================= \makeatletter \ExplSyntaxOn \NewDocumentCommand{\PrintUsedOnLabel}{ m }{% % Check if the reference UsedOn@&lt;LABELNAME&gt;@1 exists % Here the @1 means that <LABELNAME> has been referenced % with option UsedOn at least once \cs_if_exist:cT {r@UsedOn@#1@1} { % Store in a tmp clist all the references of the form % UsedOn@&lt;LABELNAME&gt;@&lt;NUMBER&gt; % where NUMBER between 1 and \value{LastRun@UsedOn@<LABELNAME>} % if the latter exists, otherwise until 1 % Should/will normally need two consecutive runs of pdflatex \cs_if_free:cTF {c@LastRun@UsedOn@#1} { \int_set:Nn \l_tmpa_int { 1 } } { \int_set:Nn \l_tmpa_int { \value{LastRun@UsedOn@#1} } } \int_set:Nn \l_tmpb_int { 1 } \int_while_do:nn { \l_tmpb_int <= \l_tmpa_int } { \clist_put_right:Nx \l_tmpa_clist { UsedOn@#1@\int_use:N \l_tmpb_int } \int_incr:N \l_tmpb_int } % Print UsedOn text by calling \cpageref with the parameter clist above % Arguments~of~cpageref~are: % \par\clist_use:Nn \l_tmpa_clist {\par}\par \emph{(Used~on~\cpageref{\l_tmpa_clist}.)} \% } }% \NewDocumentCommand{\UsedOnProcessor}{ o m }{% \IfValueT{#1}{ \str_if_eq:nnTF {#1} {UsedOn} { % Loop through (potential) label list in arg of \cref (or \Cref) \seq_set_from_clist:Nn \l_tmpa_seq {#2} \seq_map_inline:Nn \l_tmpa_seq { % if the label has not been referenced yet, % create a counter and save the label in a global key sequence \seq_if_in:NxF \g_UsedOn_k_seq {UsedOn@##1} { \newcounter{ThisRun@UsedOn@##1} \cs_if_free:cT {c@LastRun@UsedOn@##1} { \newcounter{LastRun@UsedOn@##1} } \seq_gput_right:Nx \g_UsedOn_k_seq {UsedOn@##1} } % increase the counter and compare with max counter \stepcounter{ThisRun@UsedOn@##1} \setcounter{LastRun@UsedOn@##1}{% \fp_eval:n { max(% \value{ThisRun@UsedOn@##1},% \value{LastRun@UsedOn@##1} ) }% } % store the value in global key-value property list \prop_gput:Nxx \g_UsedOn_kv_prop {UsedOn@##1} {\arabic{LastRun@UsedOn@##1}} % create a label for the UsedOn reference and number this label \origlabel{UsedOn@##1@\arabic{ThisRun@UsedOn@##1}} } } { \msg_fatal:nn {UsedOn} {Fatal~spelling~error~when~passing~option~UsedOn~to~cref~or~Cref} } } } \NewDocumentCommand{\crefUsedOn}{ s o m }{% \IfBooleanTF{#1}{ \origcref{#3} }{ \origcref{#3} }% \UsedOnProcessor[#2]{#3} }% \NewDocumentCommand{\CrefUsedOn}{ s o m }{% \IfBooleanTF{#1}{ \origCref{#3} }{ \origCref{#3} }% \UsedOnProcessor[#2]{#3} }% \NewDocumentCommand{\ReadFromAux}{ }{%
% for each key create and set a counter \prop_map_inline:Nn \g_UsedOn_kv_prop { \newcounter{LastRun@##1} \setcounter{LastRun@##1}{##2} } }% \NewDocumentCommand{\WriteToAux}{ }{% % Clear global key-value prop list \g_UsedOn_kv_prop % and rebuilt with info from current run \prop_clear:N \g_UsedOn_kv_prop \seq_map_inline:Nn \g_UsedOn_k_seq { \prop_gput:Nxx \g_UsedOn_kv_prop {##1}{\arabic{ThisRun@##1}} } % turn on expl3 functionality in aux file
\iow_now:cx { @auxout } { \token_to_str:N \ExplSyntaxOn } % loop through prop list and write to aux file \prop_map_inline:Nn \g_UsedOn_kv_prop { \iow_now:cx { @auxout } { \prop_gput_from_keyval:Nn \token_to_str:N \g_UsedOn_kv_prop {##1=##2} } } % turn off expl3 functionality in aux file
\iow_now:cx { @auxout } { \token_to_str:N \ExplSyntaxOff } }%

% global key sequence \g_UsedOn_k_seq: % container for UsedOn label names \seq_new:N \g_UsedOn_k_seq % global key-val prop list \g_UsedOn_kv_prop: % container for UsedOn label names and their LastRunValue \prop_new:N \g_UsedOn_kv_prop

%Read from .aux file and patch commands \AtBeginDocument{% \ReadFromAux % Patch label and cref to include the new UsedOn capabilities \let\origlabel\label% \let\origcref\cref% \let\origCref\Cref% \RenewDocumentCommand{\label}{ m }{% \origlabel{#1}\PrintUsedOnLabel{#1}% }% \let\cref\crefUsedOn \let\Cref\CrefUsedOn }% % Write to .aux file \AtEndDocument{% \WriteToAux }% \ExplSyntaxOff \makeatother %========================================= % -------- END Overload label and cref ---------- %========================================= \begin{document} \section{A new section} \label{sec:TestSection} Some text is written here. \begin{theorem} \label{thm:TestTheorem} The identity \begin{equation} 1=1 \end{equation} is true. \end{theorem}

\begin{lemma} \label{lemma:TestLemma} The identity \begin{equation} 2=2. \end{equation} is also true. \end{lemma}

\subsection{A new subsection}

\par The statements in \cref[UsedOn]{thm:TestTheorem} is true and has been called with UsedOn. \ \par The statements in \cref[UsedOn]{lemma:TestLemma} is true and has been called with UsedOn. \

\clearpage . \clearpage . \clearpage \par The statements in \cref[UsedOn]{thm:TestTheorem,lemma:TestLemma} are true and have been called with UsedOn. \

\clearpage \par \Cref[UsedOn]{thm:TestTheorem} is a true statement and has been called with UsedOn. \ \par \Cref[UsedOn]{lemma:TestLemma} is a true statement and has been called with UsedOn. \

\clearpage \par \Cref[UsedOn]{thm:TestTheorem,lemma:TestLemma} are true statements and have been called with UsedOn. \

\subsection{Another subsection} Here is yet another theorem. \begin{theorem} \label{thm:TestTheoremSecond} Content of theorem \end{theorem}

The statement in \cref{thm:TestTheoremSecond} is also true but it has \emph{not} been called with UsedOn.

\subsection{Yet another subsection} Printing counters. \ Note that \emph{thm:TestTheoremSecond} does \emph{not} have a UsedOn counter. \ExplSyntaxOn \begin{itemize} \prop_map_inline:Nn \g_UsedOn_kv_prop { \item The~value~of~the~counter~\emph{#1}~is~#2. } \end{itemize} \ExplSyntaxOff \end{document}

Sven Pistre
  • 171
  • 7