0

I want to test whether a macro is (a) outside or (b) inside a \footnote command, similar to what's asked in (i) How to test if I'm currently in a footnote or not, (ii) Detect whether I'm in a \footnote?, (iii) footnote boolean: how to check whether currently in a footnote, and (iv) same command with output X in main body and Y in footnote.

The general technique is (1) define a boolean, defaulted to false; (2) redefine \footnote so that it sets the boolean to true upon entry and resets it to false when exiting. This requires a prepending and an appending to the original \footnote command.

Where my question differs from the answers given for (i)–(iii) above is that they all do the redefinition with a \let. However, though it's often forgotten, the \footnote command takes an optional argument (for the number of the footnote). (E.g., see "\footnote" of LaTeX2e unofficial reference manual (October 2018).) This fact should make using \let inappropriate. (E.g., "Remember that one must never use the old trick \let\ORIxyz\xzy… if \xyz has been defined with an optional argument." xpatch documentation)

(The answer in (iv) redefines \footnotetext rather than \footnote, which I don't understand.)

Thus, I want to redefine \footnote using the commands \xpretocmd and \xapptocmd from the xpatch package (to prepend and append, respectively). (See Enrico's helpful explanation.)

The below MWE is my attempt to solve this problem.

If I comment out the appending \xapptocmd{\footnote}{\togglefalse{inFootnoteCommand}}{}{} command, it works well (except for the obvious problem that it doesn't reset the boolean to false and, hence, thinks it's in the \footnote even after it's exited). See this output:

enter image description here

But when I leave the appending command uncommented, (a) it fails to reset the boolean and (b) the output falls apart both in the body text and in the footnote text, and (c) I get the error, associated with the \footnote commands itself:

Missing \endcsname inserted. \unskip I.38 \footnote [Here's the footnote:\amIInAFootnote] The control sequence marked should not appear between \csname and \endcsname.

Here's the output: enter image description here

Where am I going wrong?

Here's the MWE:

\documentclass{article}
\usepackage{xcolor}
\usepackage{xpatch}
\usepackage{etoolbox}

\newtoggle{inFootnoteCommand}
\togglefalse{inFootnoteCommand}

\newcommand{\amIInAFootnote}{%
    \iftoggle{inFootnoteCommand}{%
        \textcolor{blue}{You are in a footnote.}
    }{%
        \textcolor{red}{You are NOT in a footnote.}
    }%
}

\parindent=0pt

\begin{document}
This line intentionally left blank %To move the text closer to the footnote
\vspace{400pt}

\xpretocmd{\footnote}{\toggletrue{inFootnoteCommand}}{SUCCESS\\}{FAIL\\}
\xapptocmd{\footnote}{\togglefalse{inFootnoteCommand}}{SUCCESS\\}{FAIL\\}

Before a footnote: \amIInAFootnote.

At the end of this sentence is a footnote:%
    \footnote{Here’s the footnote: \amIInAFootnote}

Now, I'm back from the footnote: \amIInAFootnote
\end{document}
  • 2
    like most thinks a footnote is set in a local group so you have no need to reset at the end, everything will reset at that point anyway. – David Carlisle Mar 6 '19 at 8:01
2

If you look at the \footnote definition, you'll find the following:

> \footnote=macro:
->\@ifnextchar [\@xfootnote {\stepcounter \@mpfn \protected@xdef \@thefnmark {\
thempfn }\@footnotemark \@footnotetext }.

This means that it never tries to process the fottnote text as its argument, but simply delegates this to \@footnotetext. When you patch \footnote, your \togglefalse becomes the \@footnotetext argument and messes things up. To fix it it's sufficient to apply your patches directly to \@footnotetext:

\documentclass{article}
\usepackage{xcolor}
\usepackage{xpatch}
\usepackage{etoolbox}

\newtoggle{inFootnoteCommand}
\togglefalse{inFootnoteCommand}

\newcommand{\amIInAFootnote}{%
    \iftoggle{inFootnoteCommand}{%
        \textcolor{blue}{You are in a footnote.}
    }{%
        \textcolor{red}{You are NOT in a footnote.}
    }%
}

\parindent=0pt

\begin{document}
This line intentionally left blank %To move the text closer to the footnote
\vspace{400pt}

\makeatletter
\xpretocmd{\@footnotetext}{\toggletrue{inFootnoteCommand}}{SUCCESS\\}{FAIL\\}
\xapptocmd{\@footnotetext}{\togglefalse{inFootnoteCommand}}{SUCCESS\\}{FAIL\\}
\makeatother

Before a footnote: \amIInAFootnote.

At the end of this sentence is a footnote:%
    \footnote{Here’s the footnote: \amIInAFootnote}

Now, I'm back from the footnote: \amIInAFootnote
\end{document}

The result:

enter image description here

  • Given David Carlisle's comment that "You do not need to reset at the end, simply set the toggle within the group," which does work in his answer, I thought I'd be able to comment out the \xapptocmd command that resets the boolean. But when I did so, it thinks you're in the footnote even after you left. That's puzzling. – Jim Ratliff Mar 7 '19 at 0:09
  • This works perfectly. So does @DavidCarlisle's. I had to do a mental coin toss, slightly weighted in your favor because using the xpatch is a more-familiar syntax for me than the more-TeXish \def command syntax. So I'm awarding the question's answer to you. – Jim Ratliff Mar 7 '19 at 0:11
2

If you add

\show\footnote

after your patches you will find

> \footnote=\protected macro:
->\toggletrue {inFootnoteCommand}\@ifnextchar [\@xfootnote {\stepcounter \@mpfn
 \protected@xdef \@thefnmark {\thempfn }\@footnotemark \@footnotetext }\togglefalse {inFootnoteCommand}.

That is you are seting the toggle to true and false before the footnote argument is seen and breaking the look ahead for an optional argument as \@ifnextchar is always going to see \togglefalse

You do not need to reset at the end, simply set the toggle within the group, or in the common case that the only text set at footnotesize are footnotes you don't need a toggle at all, you can simply test the font size.

enter image description here

\documentclass{article}
\usepackage{xcolor}
\usepackage{etoolbox}

\newtoggle{inFootnoteCommand}
\togglefalse{inFootnoteCommand}

\makeatletter
\let\saved@makefntext\@makefntext
\def\@makefntext#1{\saved@makefntext{\toggletrue{inFootnoteCommand}#1}}
\parindent=0pt

\begin{document}
This line intentionally left blank %To move the text closer to the footnote
\vspace{400pt}

\newcommand{\amIInAFootnote}{%
    \iftoggle{inFootnoteCommand}{%
        \textcolor{blue}{You are in a footnote.}%%%
    }{%
        \textcolor{red}{You are NOT in a footnote.}%%%
    }%
}
Before a footnote: \amIInAFootnote.

At the end of this sentence is a footnote:%
    \footnote{Here’s the footnote: \amIInAFootnote}

Now, I'm back from the footnote: \amIInAFootnote
\end{document}
  • David, (A) I think the xs in #1xs (in the \def command) is a typo. (It shows up erroneously at the end of the footnote text. (B) Since \footnotetext can also take an optional argument, I'm curious why this approach (of using \let) seems to work fine even when you use an optional argument on the original \footnote. (It does work, I checked.) I'm guessing that the optional argument to \footnote actually becomes the mandatory argument by the time it's passed to \footnotetext – Jim Ratliff Mar 6 '19 at 23:56
  • 1
    @JimRatliff oops xs deleted. yes \@makefntext is the internal command used by \footnote and \footnotetext and is (unlike those two) defined in the class file to set up the class specific style for footnotes, whereas \footnote itself is defined in the latex format. – David Carlisle Mar 7 '19 at 0:23
  • David, your method was working fine for me until I tried a multi-paragraph footnote. If you change \footnote{Here’s the footnote: \amIInAFootnote} to \footnote{Here’s the footnote: \amIInAFootnote \par Here's a second paragraph in same footnote.} things fall apart: The footnote text omits everything before the \par, including the footnote number itself. There's also a Runaway Argument error regarding {\rule \z@\footnotesep\ignorespaces. Sergei Golovan's answer using xpatch does work successfully with multiple paragraphs. – Jim Ratliff Mar 16 '19 at 23:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.