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?

1 Answer 1

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).
7
  • 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, 2020 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, 2020 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, 2020 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, 2020 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, 2020 at 18:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .