4

Suppose I'd like to patch a very complicated macro, say \autocite. For this, xpatch package is my friend. If the patch consists in applying a modal command which doesn't take arguments, such as \bfseries, \xpretocmd is also my friend:

\documentclass{article}
\usepackage{xpatch}
\usepackage[backend=biber]{biblatex}%
%
\addbibresource{biblatex-examples.bib}
%
% \autocite's patch
\xpretocmd{\autocite}%
{\bfseries}%
{\message{^^Jxpretocmd OK^^J^^J}}%
{\message{^^Jxpretocmd not OK^^J^^J}}
%
\begin{document}
\autocite{cicero}
\printbibliography
\end{document}

Now, suppose the patch consists in applying a macro which does take an argument, such as \textbf: I want \autocite... to become \textf{\autocite...}, whatever are the star/optional/mandatory arguments in .... For this, I can't use \textf{ and } as ⟨prepend⟩ and ⟨append⟩ in:

\xpretocmd{⟨command⟩}{⟨prepend⟩}{⟨success⟩}{⟨failure⟩}
\xapptocmd{⟨command⟩}{⟨append⟩}{⟨success⟩}{⟨failure⟩}

because of unbalanced braces. If { and } are replaced by \bgroup and \egroup:

\documentclass{article}
\usepackage{xpatch}
\usepackage[backend=biber]{biblatex}%
%
\addbibresource{biblatex-examples.bib}
%
% \autocite's patch
\xpretocmd{\autocite}%
{\textbf\bgroup}%
{\message{^^Jxpretocmd OK^^J^^J}}%
{\message{^^Jxpretocmd not OK^^J^^J}}
\xapptocmd{\autocite}
{\egroup}%
{\message{^^Jxapptocmd OK^^J^^J}}%
{\message{^^Jxapptocmd not OK^^J^^J}}
%
\begin{document}
\autocite{cicero}
\printbibliography
\end{document}

it doesn't work neither as \egroup is considered as the \autocite's argument.

Hence my question: how to patch with \xpretocmd and \xapptocmd a command by applying to it a macro with argument?

Edit. I placed a great deal of hope in the following trick, relying on \BODY command from environ package, but it fails as well...

\documentclass{article}
\usepackage{xpatch}
\usepackage{environ}
\usepackage[backend=biber]{biblatex}%
%
\addbibresource{biblatex-examples.bib}
%
\NewEnviron{boldify}{%
  \textbf{\BODY}%
}
% \autocite's patch
\xpretocmd{\autocite}%
{\boldify}%
{\message{^^Jxpretocmd OK^^J^^J}}%
{\message{^^Jxpretocmd not OK^^J^^J}}
\xapptocmd{\autocite}
{\endboldify}%
{\message{^^Jxapptocmd OK^^J^^J}}%
{\message{^^Jxapptocmd not OK^^J^^J}}
%
\begin{document}
\autocite{cicero}
\printbibliography
\end{document}
Denis Bitouzé
  • 9,652
  • 4
  • 27
  • 85
  • I don't think that appending to a macro with several arguments such as \autocite is easily possible… \renewcommand\citesetup{\bfseries} does not suffice for your needs? – cgnieder Nov 06 '15 at 11:18
  • @clemens \textbf is just an example. In the real life, the macro with argument is not so simple: namely, it is e.g. \switchocg{⟨refocgs⟩}{⟨complicated command such as \autocite with possibly several optional/mandatory arguments⟩}. – Denis Bitouzé Nov 06 '15 at 11:23
  • Sorry, but you can't do it like that, because \autocite ends with a macro looking for its arguments. – egreg Nov 06 '15 at 11:23
  • @DenisBitouzé then you probably need a wrapper macro for \autocite with the same argument structure which passes its arguments to \autocite and wraps the whole thing in the macro you need – cgnieder Nov 06 '15 at 11:26
  • @egreg Do you see another way (without tricks such as \let\origautocite\autocite and \renewcommand{\autocite}[...]{...} because it requires to know what are the several optional/mandatory arguments of the command to be patched)? – Denis Bitouzé Nov 06 '15 at 11:27
  • @clemens That exactly what I wanted to avoid, see my comment to egreg :) Having to deal with the exact argument structure of these macros is a pain, especially if numerous of them have to be patched – Denis Bitouzé Nov 06 '15 at 11:29
  • 1
    Imho you are misusing patching. You should better make a biblatex feature request that is adds a wrapper which allows to surround the complete cite (similar to e.g. \mkbibparens in \parencite), or adds some hook \AtEveryCiteEnd which would allow you to enter the starting code of your ocg-command in \AtEveryCite and the \pdfendlink at the end. – Ulrike Fischer Nov 06 '15 at 14:03
  • @UlrikeFischer Am I misusing it in general or in its current state? My request for patching looks to me rather reasonable :) Thanks for the \AtEveryCite reminder but I guess that, just like \xpretocmd, it doesn't support arguments sich as textbf{ (or \switchocg{⟨refocgs⟩}{) and \AtEveryCiteEnd wouldn't support arguments such as } neither. Even though, there are several other commands (outside biblatex's area) I'd like to patch the same way. – Denis Bitouzé Nov 06 '15 at 15:05
  • @UlrikeFischer Features requested: https://github.com/plk/biblatex/issues/352 and https://github.com/plk/biblatex/issues/353. – Denis Bitouzé Nov 06 '15 at 15:29
  • In the current state. You are obviously trying to adapt a large number of biblatex commands and imho you should look for a "sane" solution - an official "biblatex hook". In the case of \switchocg an \At..End would work, as it is easy to define \startswitchocg and \stopswitchocg commands which doesn't use an argument. – Ulrike Fischer Nov 06 '15 at 15:29
  • @UlrikeFischer Okay but I'm trying to achieve same goal for other commands than biblatex's ones. Do you have some pointers about \start⟨macro⟩⟨stuff⟩\stop⟨macro⟩ vs \⟨macro⟩{⟨stuff⟩}? – Denis Bitouzé Nov 06 '15 at 15:44
  • 1
    @DenisBitouzé: Such things really depend on the specific command. Sometimes you can replace a command with an argument with a \switchon ...\switchoff variant but not always. You need to look at the concrete definition. – Ulrike Fischer Nov 06 '15 at 16:26

2 Answers2

4

Let's look at the definition of \autocite:

> \autocite=\protected macro:
->\blx@citecmdinit \@ifstar {\blx@citepunct {\blx@acitei@inline *}} {\blx@citepunct {\blx@acitei@inline {}}}.

This means that the macro looks for * and then passes control to another macro. It's simply not possible to make what you want by just patching \autocite.

Sorry.

You may instead redefine \autocite using xparse so that you pass the full set of arguments and *-variants, adding \textbf around the whole thing.

The user level syntax of \autocite is

\autocite*[<prenote>][<postnote>]{<key>}

so a reimplementation with xparse could be

\usepackage{xparse}

\let\BLautocite\autocite
\RenewDocumentCommand{\autocite}{soom}{%
 \textbf{%
  \IfNoValueTF{#2}% no notes
   {\IfBooleanTF{#1}{\BLautocite*{#4}}{\BLautocite{#4}}
   {% else
    \IFNoValue{#3}% only optional argument is postnote
     {\IfBooleanTF{#1}{\BLautocite*[#2]{#4}}{\BLautocite[#2]{#4}}}
     {\IfBooleanTF{#1}{\BLautocite*[#2][#3]{#4}}{\BLautocite[#2][#3]{#4}}}%
   }%
 }%
}

You should do similarly for \Autocite.

Of course, something like this would be impossible for \autocites, where the number of arguments is variable.

More likely, you should add \begingroup\bfseries and a matching \endgroup where the actual typesetting takes place. This of course requires going deep in analyzing the working of biblatex.

egreg
  • 1,121,712
  • With xparse? But anyway, too bad: as said in comment @clemens, having to deal with the exact argument structure of these macros is a pain, especially if numerous of them have to be patched. – Denis Bitouzé Nov 06 '15 at 11:33
  • @DenisBitouzé with xparse it is a lot easier to redefine with the exact argument structure. (What actually really might be a problem is the scanning ahead for trailing punctuation that biblatex's cite commands do…) – cgnieder Nov 06 '15 at 11:48
  • @clemens Okay but, as shown by egreg, this requires to deal with this exact argument structure, a pain I'd like to avoid :) – Denis Bitouzé Nov 06 '15 at 12:37
  • @DenisBitouzé of course it does but not using xparse would even increase the pain ;) – cgnieder Nov 06 '15 at 12:39
  • @egreg Okay but I'd like to avoid tricks such as \let\origautocite\autocite and \renewcommand{\autocite}[...]{...} (LaTeX's way) or \RenewDocumentCommand{\autocite} (LaTeX3's way) because, in reimplementations, you have to take care of the exact argument structure. – Denis Bitouzé Nov 06 '15 at 12:41
3

In general, this really depends on a number of things...

For example, you can do something as simple as

enter image description here

\documentclass{article}

% Special command that is already defined
\newcommand{\specialcmd}[1]{\textit{#1}}

\begin{document}

\specialcmd{abc}

\let\oldspecialcmd\specialcmd% Copy definition
\renewcommand{\specialcmd}[1]{\textbf{\oldspecialcmd{#1}}}

\specialcmd{abc}

\end{document}

In the above, it may be known that some \specialcmd takes only a single argument. Therefore, a copy-and-redefinition approach is sufficient in most cases. However, if \specialcmd forms part of some chain of macros that conditions on possibly having a * suffix, or optional arguments, you may have to search for the end-of-chain macro to patch using \apptocmd:

enter image description here

\documentclass{article}

\usepackage{etoolbox}

\begin{document}

\section{abc}
\section*{def}

\makeatletter
\pretocmd{\section}{\bgroup\let\bfseries\mdseries}{}{}
\apptocmd{\@xsect}{\egroup}{}{}
\makeatother

\section{abc}
\section*{def}

\end{document}

Note that the above patch also relies on the fact that the change is based on something that can be defined within a scope - \bfseries - yet it also has a macro-type counterpart - \textbf.

While the above patch is implemented using etoolbox, the same principle holds for using xpatch.

So, ultimately, ones choice of patching depends on what the patch is and/or whether the macro construction is elementary or not.

In the case of \autocite, it conditions on whether you use \autocite or \autocite*, grabbing its arguments at a later stage, making it "not elementary". If you're never using the conditional \autocite*, you can get away with something as simple as

\let\oldautocite\autocite
\renewcommand{\autocite}[1]{\textbf{\oldautocite{#1}}}
Werner
  • 603,163
  • 2
    The above suggestion ignores the fact that biblatex provides \citesetup. As such, the discussion is only used for illustrative purposes. – Werner Nov 06 '15 at 11:46
  • The point is the macro (with mandatory argument) I have to apply to the command to be patched hasn't any counterpart that can be defined within a scope: it is e.g. \switchocg{⟨refocgs⟩}{⟨complicated command such as \autocite with possibly several optional/mandatory arguments⟩} (from ocgx package). – Denis Bitouzé Nov 06 '15 at 11:46
  • 1
    @DenisBitouzé: There remains a number of ways to complete the patch. A copy-and-redefinition approach may suffice where, instead of \switchocg{..}{...} you redefine \switchocg to always be \switchocg{..}{\specialcmd{...}}. – Werner Nov 06 '15 at 11:53
  • Redefining \switchocg is not be an option because \switchocg{...}{⟨complicated command such as \autocite with possibly several optional/mandatory arguments⟩} is supposed to apply to several complicated commands, such as \autocite, \parencite, \cite, \textcite, etc. (and these, only in biblatex's area but there are other areas). – Denis Bitouzé Nov 06 '15 at 12:35