TeX - LaTeX Asked on March 2, 2021
TeX FAQ’s "Patching existing commands" gives a method to add to existing commands that have an optional argument:
documentclass{article}
usepackage{letltxmacro}
newcommand{rough}[2][default]{...}
LetLtxMacro{OldRough}{rough}
renewcommand{rough}[2][newdef]{mumbleOldRough[{#1}]{#2}}
However, it assumes that you want a new default argument "newdef
". If the default argument is to be kept, you may change "newdef
" to "default
", but that’s only possible if you have internal knowledge of the previously defined command. Furthermore, if the that value is modified, you have to change it in your patching as well.
Is it possible to add to an existing command with optional arguments in a way that the default argument is kept, even if you don’t know what that argument is?
Note: The argument has to still be usable inside the new definition.
Edit: After trying the suggested xapptocmd
and xapptocmd
I was still unable to reproduce the desired result in the command I’m working with, clevelref
‘s label
.
I would expect the code below to write "aaa", and then apply the label, which is not the case, even when replaced by things such as math, the contents are still not present in the final document:
documentclass[]{article}
usepackage{cleveref,xpatch}
xpretocmdlabel{aaa}{}{}
begin{document}
label{example}
end{document}
This gives me the impression that it’s not being correctly appended.
Also, when trying to access the second argument of label
, TeX throws the error Illegal parameter number in definition of etb@resrvda. xpretocmdlabel{#1#2}
:
documentclass[]{article}
usepackage{cleveref,xpatch}
xpretocmdlabel{#1#2}{}{}
begin{document}
end{document}
I presume it is because it is detecting the standard definition of label
, which only has a single parameter, instead of cleverf
‘s one.
How may these be corrected?
What does LaTeX do when processing the definition of a command with an optional argument? Say we have
newcommand{test}[2][default]{Optional: #1; mandatory: #2.}
With a rather long sequence of steps that's not relevant for the discussion, the basic working is to do
deftest{@protected@testopttesttest{default}}
expandafterdefcsnamestringtestendcsname[#1]#2{Optional: #1; mandatory: #2.}
In the first line, test
represents a single token with a backslash in its name, which can be obtained by csnamestringtestendcsname
.
The macro @protected@testopt
branches according to whether protect
is relax
or not and either calls @testopt
(removing the else...fi
part) or @x@protecttest
(which removes everything up to fi
and only leaves protecttest
). The macro @testopt
checks for a following [
and does the right thing.
So, if you want to patch the real replacement text of such a command you need to do
expandafterpatchcmdcsnamestringtestendcsname
{<search>}
{<replace>}
{<success>}{<fail>}
It would be worse if you have
DeclareRobustCommand{test}[2][default]{Optional: #1; mandatory: #2.}
because in this case you'd need to do
expandafterpatchcmdcsnamestringtestspaceendcsname
{<search>}
{<replace>}
{<success>}{<fail>}
For no optional argument, in the case of DeclareRobustCommand{test}[<n>]{...}
the call should be
expandafterpatchcmdcsname test endcsname
{<search>}
{<replace>}
{<success>}{<fail>}
This is the reason why I wrote the xpatch
package, which is able to test how the command was defined to begin with and calls patchcmd
with the right incantation. It also supports newrobustcmd
from etoolbox
and has built-in tools for patching internals of biblatex
.
So you can simply do
usepackage{xpatch}
xpatchcmd{test}
{<search>}
{<replace>}
{<success>}{<fail>}
and it will work in all cases.
What if you want to patch the default value for the optional argument? Even without knowing what the default value is?
documentclass{article}
usepackage{etoolbox}
makeatletter
defreplaceoptionalargument#1#2#3#4{%
ifcsnamestring#1endcsname
@replaceoptionalargument#1{#2}{#3}%
else
#4%
fi
}
def@replaceoptionalargument#1#2#3{%
edef#1{%
unexpandedexpandafterexpandafterexpandafter{%
expandafter@@replaceoptionalargument#1{#2}%
}%
}%
#3%
}
def@@replaceoptionalargument#1#2#3#4#5{#1#2#3{#5}}
makeatother
newcommand{test}[2][default]{}
replaceoptionalargumenttest{new}{message{YES}}{message{NO}}
label
When you're trying to patch a command, you have to know in detail how it's defined. For instance, if you do showlabel
in the preamble of your test document, you'll be presented the following information
> label=macro:
#1->@bsphack protected@write @auxout {}{string newlabel {#1}{{@currentlabel }{thepage }}}@esphack .
which is the standard LaTeX definition. Indeed, cleveref
delays the redefinition at begin document. Good, let's issue showlabel
after begin{document}
, getting
> label=macro:
->@ifnextchar [label@optarg label@noarg .
which shows that label
is not redefined like
renewcommand{label}[2][<default>]{...}
so your attempt will fail because label
has no argument at all. Why does the developer do that way? Because there is no default value for the optional argument and different code has to be executed with or without the optional argument.
Correct answer by egreg on March 2, 2021
The xpatch
package can patch commands containing optional arguments. The following changes the definition without changing the optional argument's value (and can access the parameters using #1
or #2
):
documentclass[]{article}
usepackage{xpatch}
newcommandfoo[2][default]
{%
Normal definition. Optional argument: #1. Mandatory argument: #2.%
}
begin{document}
foo{abc}
xpatchcmdfoo{Normal}{Patched}{}{}
foo{abc}
xpretocmdfoo{Optional=#1. }{}{}
foo{abc}
xapptocmdfoo{ Mandatory=#2.}{}{}
foo{abc}
end{document}
Answered by Skillmon on March 2, 2021
showrough
reveals:
> rough=macro:
->@protected@testopt rough rough {newdef }.
l.12 showrough
rough
is just a wrapper for launching the LaTeX 2e-kernel's mechanism for detecting the presence of an optional argument while taking the protect
ion-mechanism into account.
Internally the macro rough
is called for processing the arguments, thus that's the macro that needs to be patched:
documentclass{article}
newcommand{rough}[2][default]{... s.th. with #1 and #2 ...}
expandaftershowcsnamestringroughendcsname
% > rough=long macro:
% [#1]#2->... s.th. with #1 and #2 ....
% <recently read> rough
%------------------------------------------------------------
newcommandPassFirstToSecond[2]{#2{#1}}%
newcommandExchange[2]{#2#1}%
expandafterPassFirstToSecondexpandafter{%
romannumeral
expandafterexpandafterexpandafterExchange
expandafterexpandafterexpandafter{%
csnamestringroughendcsname[{#1}]{#2}%
}{0 mumbleatbegin}%
mumbleatend%
}{%
longexpandafterdefcsnamestringroughendcsname[#1]#2%
}%
expandaftershowcsnamestringroughendcsname
% > rough=long macro:
% [#1]#2->mumbleatbegin ... s.th. with #1 and #2 ...mumbleatend .
% <recently read> rough
stop
Answered by Ulrich Diez on March 2, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP