I'd use the xparse
package to do this, as everything is then 'pre-packaged':
\documentclass{article}
\usepackage{color,xparse}
\NewDocumentCommand\MyTextColor{m+g}{%
\IfNoValueTF{#2}
{\color{#1}}
{\textcolor{#1}{#2}}%
}
\begin{document}
\MyTextColor{green}{stuff}
\MyTextColor{red} Some text
\end{document}
The same can of course be done using \@ifnextchar
\documentclass{article}
\usepackage{color}
\makeatletter
\newcommand*\MyTextColor[1]{%
\@ifnextchar\bgroup
{\textcolor{#1}}
{\color{#1}}%
}
\makeatother
\begin{document}
\MyTextColor{green}{stuff}
\MyTextColor{red} Some text
\end{document}
The key point is that you need to look for {
using \bgroup
rather than trying to use it directly (as it is the begin-group character).
To explain how the \@ifnextchar
part deals with the need for either 1 or 2 arguments, what happens in my second approach is that
\newcommand*\MyTextColor[1]{%
defines a macro which absorbs one argument. So:
\MyTextColor{red}{stuff}
absorbs red
as #1
and leaves {stuff}
in the input stream, while
\MyTextColor{red} other stuff
also absorbs red
as #1
and leaves other stuff
in the input stream. We now apply \@ifnextchar
, which 'peeks' at the next token without absorbing it. The test reads
\@ifnextchar\bgroup
{\textcolor{#1}}
{\color{#1}}%
and so depending on the result, either \textcolor{red}
or \color{red}
is inserted into the input stream. Thus we end up with either
\textcolor{red}{stuff}
or
\color{red} other stuff
which means that \textcolor
will get the required two arguments, while \color
only gets one. The key is that {red}
is reinserted into the input after either \textcolor
or \color
.
Although it is not directly related to the question, a few notes on the xparse
approach may be useful. When using the xparse
package, arguments are described by letter, with m
representing a mandatory argument and g
representing an optional argument in braces (i.e. an optional TeX group). A +
before the letter allows that argument to accept paragraph tokens, so in my definition the first argument cannot (it's supposed to be the name of a colour), while the second one can (it is arbitrary text).
The g
argument returns a special token (\NoValue
) if the optional argument was not present at all. If a default value was wanted, I'd have used G{<default>}
. The same approach applies to standard LaTeX optional arguments, which are represented as o
or O
, depending on whether a default value is required. I have tested for \NoValue
with the test \IfNoValueTF
; \IfNoValueT
and \IfNoValueT
are also available.
If you add \tracingpatches
to the preamble you find the answer in the log
:
[debug] tracing \patchcmd on input line 14
[debug] analyzing '\abc'
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] -- nested patching command and parameters in patch
[debug] -> the patching command seems to be nested in the
[debug] argument to some other command
[debug] -> the patch text seems to contain # characters
[debug] -> either avoid nesting or use # characters with
[debug] category code 12 in the patch text
[debug] -> simply doubling the # characters will not work
And indeed, changing the catcode of #
to 12
is working:
\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\tracingpatches
\newcommand{\abc}[2]{#1\ #2}% Magic macro
\catcode`\#=12
\newcommand{\patchabc}{%
% \patchcmd{<cmd>}{<search>}{<replace>}{<success>}{<failure>}
\patchcmd{\abc}{#1}{#2}{}{}%
}
\catcode`\#=6
\begin{document}
\abc{A}{B}
\patchabc% Patch \abc
\abc{A}{B}
\end{document}
Best Answer
Something like this works. (It also works with
xparse
in the same way.)