1

Consider the following MWE

\documentclass{article}

\usepackage{amsthm} \usepackage{thmtools} \usepackage[nameinlink]{cleveref}

\usepackage{xparse} \ExplSyntaxOn \NewDocumentCommand{ \IfEmptyTF }{ m m m } { \sbox0{#1} \ifdim\wd0=0pt #2 \else #3 \fi } \ExplSyntaxOff

\makeatletter \declaretheoremstyle[ postheadspace=.5em, headpunct={}, notebraces={}{}, notefont=\bfseries, headformat=\IfEmptyTF{\NOTE}{\NAME~\NUMBER}{\let\thmt@space@empty\NOTE} ]{theorem} \makeatother

\declaretheorem[ style=theorem, name=Theorem ]{theorem}

\crefname{theorem}{Theorem}{Theorems}

\begin{document}

\begin{theorem}[label=thm:a] A theorem. \end{theorem}

\begin{theorem}[name=Important theorem, label=thm:b] Another theorem. \end{theorem}

\cref{thm:a} and \cref{thm:b} % <-- should be 'Theorem 1 and Important theorem'

\end{document}

which produces

enter image description here

I'd like to modify the content of \crefname{theorem}{..}{..} so that \cref{..} prints Theorem n if the nth theorem doesn't have a name, but if it does it just prints the name.

I know it can probably be done using \nameref or \autoref, but is a \cref only solution possible?

noibe
  • 2,084

1 Answers1

2

I'm not sure this is doable using the cleveref machinery, because \crefformat doesn't seem to have access to the label. Moreover, cleveref is designed to be able to combine several numbered items while not repeating the item type (e.g., “theorems 1, 3 and 10”), which doesn't fit very well with what you want to do (articles would need to be added...).

That being said, one can redefine \cref to:

  • detect if the argument consists of only one label;

  • if this is the case and if it has a name recorded by the nameref package, retrieve this name and print it (I use the great refcount package for this);

  • otherwise, let the normal \cref command handle the situation.

Unrelated: your dimension test in \IfEmptyTF is a bit weird. I reimplemented it in a better way,1 but it may be that you'd rather want to test whether the first-level expansion of the first argument of \IfEmptyTF is empty. It's easy to do, but I kept the principle of “boxing + dimension test” in case this matters for your real life documents.

\documentclass{article}
\usepackage{letltxmacro}
\usepackage{nameref}
\usepackage{refcount}
\usepackage{amsthm}
\usepackage{thmtools}
\usepackage[nameinlink]{cleveref}
\usepackage{xparse}

\ExplSyntaxOn \NewDocumentCommand { \IfEmptyTF } { m } { \hbox_set:Nw \l_tmpa_box #1 \hbox_set_end: \dim_compare:nNnTF { \box_wd:N \l_tmpa_box } = { \c_zero_dim } % The T and F clauses are taken from what follows in the input stream }

% Save the original \cref commmand \LetLtxMacro{__noibe_orig_cref:n}{\cref}

\tl_new:N \l__noibe_theorem_name_tl

\RenewDocumentCommand{ \cref }{ m } { \int_compare:nNnTF { \clist_count:n {#1} } > { 1 } { __noibe_orig_cref:n {#1} } { \exp_args:NNo \tl_set:No \l__noibe_theorem_name_tl { \getrefbykeydefault {#1} { name } { } } \tl_if_empty:NTF \l__noibe_theorem_name_tl { __noibe_orig_cref:n {#1} } { \l__noibe_theorem_name_tl } } } \ExplSyntaxOff

\makeatletter \declaretheoremstyle[ postheadspace=.5em, headpunct={}, notebraces={}{}, notefont=\bfseries, headformat=\IfEmptyTF{\NOTE}{\NAME~\NUMBER}{\let\thmt@space@empty\NOTE} ]{theorem} \makeatother

\declaretheorem[ style=theorem, name=Theorem ]{theorem}

\crefname{theorem}{Theorem}{Theorems}

\begin{document}

\begin{theorem}[label=thm:a] A theorem. \end{theorem}

\begin{theorem}[name=Important theorem, label=thm:b] Another theorem. \end{theorem}

\cref{thm:a} and \cref{thm:b} % “Theorem 1 and Important theorem”

\cref{thm:a,thm:b} % “Theorems 1 and 2”

\end{document}

enter image description here

Problematic example for your \IfEmptyTF

Here is a simple example where my implementation of \IfEmptyTF works fine whereas yours produces an error:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn \NewDocumentCommand{ \YourIfEmptyTF }{ m m m } { \sbox0{#1} \ifdim\wd0=0pt #2 \else #3 \fi }

\NewDocumentCommand { \MyIfEmptyTF } { m } { \hbox_set:Nw \l_tmpa_box #1 \hbox_set_end: \dim_compare:nNnTF { \box_wd:N \l_tmpa_box } = { \c_zero_dim } } \ExplSyntaxOff

\begin{document}

%\YourIfEmptyTF{}{\textbf}{\textit}{foo bar} % Error: Too many }'s.

\MyIfEmptyTF{}{\textbf}{\textit}{foo bar} % 'foo bar' is typeset in bold

\MyIfEmptyTF{non-empty}{\textbf}{\textit}{foo bar} % 'foo bar' is typeset in italics

\end{document}

enter image description here


Footnote

  1. No risk of expanding the start of #2 when reading the second ⟨dimen⟩ and, more importantly, allow each of the T and F clauses to act on tokens that follow the \IfEmptyTF{...}{T}{F} in the input stream (with your code, #2 is followed by \else in the input stream, and #3 is followed by \fi, which can be a showstopper when you want to implement certain things—this is demonstrated under Problematic example for your \IfEmptyTF in this answer).
frougon
  • 24,283
  • 1
  • 32
  • 55
  • I didn't think about how it should behave with multiple references, I guess in that case I'd just do it by hand (\cref{thm:a} and \cref{thm:b}). Btw, I must admit I copied the implementation of \IfEmptTF from this post. – noibe Oct 12 '20 at 17:24
  • You copied the implementation from egreg's post, but 1) you put it after ExplSyntaxOn, which changes the meaning of spaces (in egreg's post, TeX will see a space token after =0pt which is definitely desirable; in the code from your question, it won't, which is why the first token of #2 will be expanded and possibly more, regardless of whether the outcome of the test is true or false. 2) In egreg's post, the then and else clauses are hardcoded in the command and written this way, won't try to read any of the \else and \fi tokens. – frougon Oct 12 '20 at 17:59
  • Your \IfEmptyTF command instead grabs these clauses as arguments, thus one can expect them to contain anything. It would work for self-contained then and else clauses, but if you want to use something that grabs one or more arguments or simply looks ahead in the input, and the arguments or interesting input are after the \IfEmptyTF{...}{T}{F} code, then you'll need “my” technique with \use_i:nn and \use_ii:nn (which correspond to \@firstoftwo and \@secondoftwo in LaTeX2e). – frougon Oct 12 '20 at 17:59
  • A silly example to help you understand what I mean could be \IfEmptyTF{whatever}{\textbf}{\textit}{⟨text that should be set in bold or in italics⟩}. With your technique, \textbf woul be applied to \else and \textit to \fi. With mine, the selected command will be applied to ⟨text that should be set in bold or in italics⟩. Bottom line: \ifdim is a TeX primitive; you need to know a bit how it works to avoid pitfalls. TeX conditionals don't work like conditionals in general-purpose programming languages. – frougon Oct 12 '20 at 18:13
  • I've edited my answer to add a concrete example where my implementation of \IfEmptyTF works fine whereas yours produces an error. – frougon Oct 12 '20 at 18:33
  • Thanks, I'll check that out later. – noibe Oct 12 '20 at 18:56
  • Okay, no problem. In case you wonder, I removed \use_i:nn and \use_ii:nn from my implementation of \IfEmptyTF; the behavior is exactly the same as before, but a bit more efficient. And if you understand how it works, you'll understand why TeX is different from most programming languages, due to the fact that it is a macro language (when expanded, a macro grabs its arguments and is replaced by its replacement text, with the arguments substituted for the respective parameters in the replacement text). – frougon Oct 12 '20 at 19:15