6

In a LaTeX document, I use the fancyref package for in-document references. However, I need a bunch of custom prefixes to reference lemmas, definitions and theorems, which fancyref dos not provide. Following the documentation, adding a prefix which supports both vario and plain for both fref and Fref takes a lot of text, and it's all the same all the time. I'd like to avoid this using a macro which does it all for me, and I got stuck with that.

Here's my current attempt:

\documentclass[a4paper]{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[english]{babel}
\usepackage[plain]{fancyref}

% command to define a fancyref prefix
\makeatletter
\def\mkfancyprefix#1#2{%
  \@namedef{fancyref#1labelprefix}{#1}%
  \frefformat{plain}{\@nameuse{fancyref#1labelprefix}}{%
    \MakeLowercase{#2}\fancyrefdefaultspacing##1%
  }%
  \Frefformat{plain}{\@nameuse{fancyref#1labelprefix}}{%
    #2\fancyrefdefaultspacing##1%
  }%
}
\makeatother

% environments and references for lemmas, ...
\newtheorem{lemma}{Lemma}
\mkfancyprefix{lem}{Lemma}

% content
\begin{document}
\begin{lemma}
\label{lem:lemma}
This is a Lemma
\end{lemma}
There's \fref{lem:lemma} around! \Fref{lem:lemma} is the same, but at the beginning of a sentence.
\end{document}

However, this fails to compile, complaining about a:

Missing \begin{document}

If I move the \mkfancyprefix{lem}{Lemma} after \begin{document}, I can see that it actually outputs fancyreflemlabelprefixfancyreflemlabelprefix. Other than that, everything works and looks as expected.

This confuses me a lot, why does the command print the expanded arguments of \@nameuse to the document? I did some experimenting and added \expandafter immediately before both occurrences of \@nameuse. That changes the output to lemlem, which does not reduce my confusion.

Why does it do that, and how can I prevent it?

Addendum:
Enrico provided a great answer to my question below. Expanding this a little yields the a command which declares a new fancyref prefix in the format variants vario and plain. If you use others (like main), you have to extend the macro accordingly.

\makeatletter
\def\mkfancyprefix#1#2{%
\expandafter\def\csname fancyref#1labelprefix\endcsname{#1}%
% plain lowercase
\begingroup\def\x{\endgroup\frefformat{plain}}%
    \expandafter\x\csname fancyref#1labelprefix\endcsname
    {\MakeLowercase{#2}\fancyrefdefaultspacing##1}%
% plain uppercase
\begingroup\def\x{\endgroup\Frefformat{plain}}%
    \expandafter\x\csname fancyref#1labelprefix\endcsname
    {#2\fancyrefdefaultspacing##1}%
% vario lowercase
\begingroup\def\x{\endgroup\frefformat{vario}}%
    \expandafter\x\csname fancyref#1labelprefix\endcsname
    {\MakeLowercase{#2}\fancyrefdefaultspacing##1##3}%
% vario uppercase
\begingroup\def\x{\endgroup\Frefformat{vario}}%
    \expandafter\x\csname fancyref#1labelprefix\endcsname
    {#2\fancyrefdefaultspacing##1##3}%
}
\makeatother
Ralf Jung
  • 203
  • 1
  • 6
  • Welcome to TeX.SX. Note that you don't have to sign with your name since it automatically appears in the lower right corner of your post. – Claudio Fiandrino Mar 03 '13 at 18:17
  • The second argument of \frefformat needs to be a csname which \@nameuse only gives after two expansions. \@nameuse{foo} first expands to \csname foo\endcsname and then to \foo – cgnieder Mar 03 '13 at 18:18
  • 1
    Have you considered using the cleveref package instead of fancyref? The cleveref package should have predefined names for all of the objects you mention. – Mico Mar 03 '13 at 18:20
  • @cgnieder: I also tried putting \csname fancyref#1labelprefix\endcsname but then it says Extra \endcsname. – Ralf Jung Mar 04 '13 at 09:32
  • @Mico: Unfortunately, cleverref can not automatically print the page reference the way fancyref does: Using "on the next page", leave it away when it is on the same page, and the page number otherwise. (Also, sorry for adding three comments here and constantly editing them, somehow it wouldn't let me add a newline in a comment, and automatically post it as the browser looses focus, which I did not expect) – Ralf Jung Mar 04 '13 at 09:35
  • @RalfJung \csname ...\endcsname should work together with an \expandafter chain – cgnieder Mar 04 '13 at 09:44
  • @cgnieder: I changed the \@namedef{...} to \expandafter\csname fancyref#1labelprefix\endcsname. Now the macro works again without using \@namedef, but it still produces output: lemlem. Should I edit the question? Also, I don't know what an \expandafter chain is, and after some searching I have the impression it's about adding \expandafter in many places, but I can't seem to figure out the right ones. Not do I even understand what I am doing. – Ralf Jung Mar 04 '13 at 09:58
  • @RalfJung - Re the use of cleveref instead of fancyref while also making use of the capabilities of the varioref package: You could type something like as shown by \cref{type:descript} \vpageref{type:descrip}. (Here, \vpageref will create an "intelligent" cross-reference to the page number.) This is admittedly not as elegant as modifying/augmenting some of fancyref's capabilities directly. But, depending of course on what you need to get done, the \cref/\vpageref combination may be good enough to satisfy to your typesetting needs. – Mico Mar 04 '13 at 17:37

1 Answers1

7

The problem is that \frefformat expects its second argument to be a control sequence, not the instructions to produce one; so you have to build the token before TeX sees \frefformat:

\makeatletter
\def\mkfancyprefix#1#2{%
  \@namedef{fancyref#1labelprefix}{#1}%
  \begingroup\def\x{\endgroup\frefformat{plain}}%
    \expandafter\x\csname fancyref#1labelprefix\endcsname
      {\MakeLowercase{#2}\fancyrefdefaultspacing##1}%
  \begingroup\def\x{\endgroup\Frefformat{plain}}%
    \expandafter\x\csname fancyref#1labelprefix\endcsname
      {#2\fancyrefdefaultspacing##1}%
}
\makeatother

The \x trick is just to be able to use \expandafter.

How does it work? When \mkfancyprefix{lem}{Lemma} is called, TeX does

\@namedef{fancyreflemlabelprefix}{lem}

which is equivalent to \def\fancyreflemlabelprefix{lem}. Then it finds

\begingroup\def\x{\endgroup\frefformat{plain}}%
  \expandafter\x\csname fancyreflemlabelprefix\endcsname
    {\MakeLowercase{Lemma}\fancyrefdefaultspacing#1}

(the ##1 in the definition of \mkfancyref now becomes #1 as it should). After doing the \def\x{...}, LaTeX still has a \begingroup pending and finds \expandafter\x, so it expands \csname fancyreflemlabelprefix\endcsname (once), which produces the token \fancyreflemlabelprefix. Next \x is expanded, so TeX is confronted with

\endgroup\frefformat{plain}\fancyreflemlabelprefix
  {\MakeLowercase{Lemma}\fancyrefdefaultspacing#1}

The \endgroup matches the still pending \begingroup and removes the definition of \x from memory; then the correct \frefformat instruction is ready for being processed.

egreg
  • 1,121,712
  • Thank you very much, the second revision of this answer is working great, both in the MWE and in my actual document (the first one consumed the space after \fref{lem:lemma} for some reason). And the explanation is very helpful. :) – Ralf Jung Mar 04 '13 at 10:17
  • @RalfJung I too was surprised because of the missing space. I really don't know why this happens, but the current version seems good. – egreg Mar 04 '13 at 10:19
  • This is brilliant! I'll add a link to this answer in my answer to the question about cross-referencing commands. – Mico Mar 04 '13 at 11:47
  • @Mico: I use this command to also declare the vario type formats (which are used by fancyref per default), but I omitted that from the question to keep it simpler. However, for your overview, the full command would IMHO be more helpful. Maybe even main should be added. How should that be handled? (I wanted add that as command over there, but somehow I can't) – Ralf Jung Mar 04 '13 at 11:55
  • @RalfJung - Providing a full example that lets the user make use of both the fancyref and varioref packages would certainly be useful. You could provide an addendum to your question, in which you provide such a code snippet. I'll happily mention this code in my answer about cross-referencing packages. – Mico Mar 04 '13 at 13:35
  • @Mico: You mean, editing the question to include the answer? This does not sound right to me, but of course I am new here. – Ralf Jung Mar 04 '13 at 17:03
  • @RalfJung - A simple restatement of Enrico's solution as an addendum to your answer wouldn't be the way to go. However, if you explain in the addendum that you're providing a solution that goes beyond what you asked for originally, and if you explain that you're providing a solution that makes the (augmented/modified) \fref commands work nicely with varioref, I think you'd be providing some useful additional information. – Mico Mar 04 '13 at 17:39