6

A command can be employed as an environment.

The macro

\newcommand{\rev}[2][blue]{{\color{#1}#2}}

simply colors its argument, for example.

Why does the corresponding environment not color the entire content?

\begin{document}
    black  % is correct
    \rev{in blue} \rev[red]{in red}  % correct
    black  % correct

    \begin{rev}in blue\end{rev}% only first letter is blue
    \begin{rev}[red]           % only first letter is red
        in red
    \end{rev}
    black again  % black again
\end{document}

As well, \rev[\normalcolor]{text} should work fine.

1
  • 5
    That's a wonderful example of why you should not rely on the environment foo calling the macro \foo internally. Your macro \rev takes a mandatory argument, and without braces it's just the first following token (in this case the first letter because spaces are discarded when looking for undelimited arguments). – campa Feb 27 at 22:45
9

It is possible to check whether the macro is used as an environment or as a normal command by checking against \@currenvir. The following uses this to either call \color (which will colour the entire contents of the environment), or \textcolor (which will only colour the mandatory argument).

\documentclass[]{article}

\usepackage[]{color}
\makeatletter
\newcommand\rev[1][blue]
  {%
    \begingroup
      \def\tmp{rev}%
      \expandafter
    \endgroup
    \ifx\tmp\@currenvir
      \expandafter\color
    \else
      \expandafter\textcolor
    \fi
    {#1}%
  }
\makeatother

\begin{document}
    black  % is correct
    \rev{in blue} \rev[red]{in red}  % correct
    black  % correct

    \begin{rev}in blue\end{rev} % only first letter is blue
    \begin{rev}[red]            % only first letter is red
        in red%
    \end{rev}
    black again  % black again
\end{document}

Note that this isn't fool-proof. A usage such as \begin{rev}abc\rev[red]{def}ghi\end{rev} will result in abc being blue, and defghi being red.


A perhaps better way to check whether rev is used as an environment is to use LaTeX's hook mechanism to set a boolean that indicates that the current rev is an environment (and let the environment version set the boolean back to false). This would also work for cases such as \begin{rev}abc\rev[red]{def}ghi\end{rev}.

\documentclass[]{article}

\usepackage[]{color}
\makeatletter
\newif\ifrev@inenv
\ifdefined\AddToHook
  \AddToHook{env/rev/begin}{\rev@inenvtrue}
\else
  \usepackage{etoolbox}
  \AtBeginEnvironment{rev}{\rev@inenvtrue}
\fi
\newcommand\rev[1][blue]
  {%
    \ifrev@inenv
      \rev@inenvfalse
      \expandafter\color
    \else
      \expandafter\textcolor
    \fi
    {#1}%
  }
\makeatother

\begin{document}
    black  % is correct
    \rev{in blue} \rev[red]{in red}  % correct
    black  % correct

    \begin{rev}in blue\end{rev} % only first letter is blue
    \begin{rev}[red]            % only first letter is red
      in red\rev[green]{test}ed%
    \end{rev}
    black again  % black again
\end{document}

(I made an edit which checks whether \AddToHook is available, and if not falls back to etoolbox)

1
  • I upvoted because it's definitely a smart TeXnical exercise but I cannot deny that I get some heartburn... – campa Feb 28 at 12:39
7
\begin{rev}[red]           % only first letter is red
        in red
    \end{rev}

is more or less

\begingroup\rev[red]
   in red
   \relax\endgroup

which is equivalent to

\begingroup\rev[red]{i}%
   n red
   \relax\endgroup

By the built in rules of parsing for tex macro arguments.


If you want to define an environment where the body of the environment acts as a macro parameter, use \NewDocumentEnvironment and a b argument. This requires the xparse package in older latex fromats but is built in to current releases.

Of course for the specific example of \color no argument is needed, just use \color{red} and the scope of the colour change will end at the end of the environment.

7
  • Thank you. I am aware of that. So one should expect that \newcommand{\rev}[2][blue]{{\color{#1}{{{#2}}}}} resolves the issue. But it does not. Is there a way to fix the problem? – Alois Pichler Feb 27 at 23:33
  • @AloisPichler no as I tried to explain #2 is the first token after \ref[red] so i here. define it as \newcommand{\rev}[1][blue]{\color{#1}} then it will work. – David Carlisle Feb 28 at 1:05
  • Thank you! I did not think about that. Unfortunately, your code does not close the environment. The mwe is still not correct. Employing \newcommand{\rev}[1][blue]{\color{#1}} displays 'black' as red and I am still struggling with the problem. – Alois Pichler Feb 28 at 8:08
  • @AloisPichler then see my answer which works in both cases... (especially the second variant, which is more robust) – Skillmon Feb 28 at 11:21
  • 3
    @Skillmon your code works but I think it's arguably architecturally wrong. The commands should have the same argument structure or have different names. – David Carlisle Feb 28 at 12:06

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.