29

When I want to globally modify a command in my document, I will often use a duplicated version of the original in the stencil, with the help of the \let macro. For example, if I wanted to do something every time I created a section, I might say:

\let\svsection\section
\renewcommand\section[1]{\mychanges\svsection{#1}}

It just dawned on me that I don't know how or if something similar can be done for environments. What I would hope for is something like (I'm omitting arguments for the sake of simplicity, don't focus on that):

\let\svfigure\figure
\renewenvironment{figure}{\blahblah\begin{svfigure}}
                         {\end{svfigure}\moreblah}

Of course, this syntax won't work because \figure isn't a command.

But, I was hoping that the creation of environment abc would always be accompanied by the creation of associated constructed commands, for example, \start@abc and \end@abc, such that these associated commands could be \let to duplicate the original environment.

I also realize that one approach may be to "patch" the original environment, but I don't think that is really what I'm asking, since I'm not sure a patch can be easily undone, whereas redoing a \let in the opposite direction (e.g., \let\section\svsection) will totally undo the effects of redefinition.

It would be a handy feature to know how to duplicate an environment, so that the original copy can be \renewed in the fashion I describe.

8
  • \begin{foo} always issues the command \foo and \end{foo} issues \endfoo defining it to \relax if it doesn't exist (which is why \begin{large}...\end{large} actually works). These commands are defined by \newenvironment{foo}{}{}
    – cgnieder
    Commented May 29, 2013 at 11:08
  • @cgnieder So you are saying \let\svfigure\figure and \let\endsvfigure\endfigure actually creates an environment svfigure which is a copy of figure? If so, post the answer and I will accept it. Commented May 29, 2013 at 11:12
  • Is your question inspired by this one? tex.stackexchange.com/questions/116651/… 'Cos I was wondering the same thing myself when I wrote my answer. Commented May 29, 2013 at 11:13
  • @JohnWickerson Yes, the cited question got me thinking about it (again). Commented May 29, 2013 at 11:16
  • @StevenB.Segletes in many cases this works. It wouldn't work for the verbatim environment or for amsmath's align environment, for example.
    – cgnieder
    Commented May 29, 2013 at 11:18

5 Answers 5

24

When you define an environment with

\newenvironment{foo}[1]{start code with #1}{end code}

what happens internally (after some checking for optional arguments, testing if the environment exists, and taking care of a possible star argument to \newenvironment) essentially boils down to

\newcommand\foo[1]{start code with #1}% actually \new@command
\def\endfoo{end code}

This means if the environment has no optional arguments

\let\myfoo\foo
\let\endmyfoo\endfoo

copies the environment {foo} as environment {myfoo}. (If it has an optional argument \LetLtxMacro should be used instead of \let, see When to use \LetLtxMacro?.)

In the article class the {figure} environment is defined as

\newenvironment{figure}
               {\@float{figure}}
               {\end@float}

so saying

\let\myfigure\figure
\let\endmyfigure\endfigure

works:

\documentclass{article}
\newenvironment{foo}[1]{start foo with (#1)}{end foo}

\let\myfoo\foo
\let\endmyfoo\endfoo

\let\myfigure\figure
\let\endmyfigure\endfigure

\begin{document}

\begin{myfoo}{arg}
  bar
\end{myfoo}

\begin{myfigure}
  copied figure
  \caption{my figure}
\end{myfigure}

\end{document}

enter image description here

Some environments like {verbatim} or AMSmath's {align} cannot be copied this way since they need to find \end{verbatim} or \end{align}, respectively, exactly.

4
  • 3
    I'd mention \LetLtxMacro for the cases when the environment has optional arguments.
    – egreg
    Commented May 29, 2013 at 15:37
  • @egreg I have! :)
    – cgnieder
    Commented May 29, 2013 at 15:38
  • Oh, sorry, I didn't see it; but I upvoted anyway. ;-) However \LetLtxMacro is not only "safer", but it's "the right tool to use".
    – egreg
    Commented May 29, 2013 at 15:39
  • Do you want to edit \LetLtxMacro to \NewCommandCopy? the linked question changed as well.
    – user202729
    Commented Mar 25, 2023 at 0:31
17

Patching \section as you do is not an especially good way to proceed, because you lose all the flexibility of the original command; one always has to look how a command is defined, before patching it. You'll discover that \section has no argument, for instance.

The same holds for environments; they are realized through a pair of commands: with

\newenvironment{foo}[1]
  {something with #1 at the start}
  {something at the end}

LaTeX defines \foo and \endfoo in a way basically equivalent to

\newcommand{\foo}[1]{something with #1 at the start}
\def\endfoo{something at the end}

In standard LaTeX the \end... command is always parameterless (and \newcommand wouldn't allow to define it).

It's also important to know when the commands are executed; here's the definition of \begin:

% latex.ltx, line 3944:
\def\begin#1{%
  \@ifundefined{#1}%
    {\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}%
    {\def\reserved@a{\def\@currenvir{#1}%
     \edef\@currenvline{\on@line}%
     \csname #1\endcsname}}%
  \@ignorefalse
  \begingroup\@endpefalse\reserved@a}

Apart from some details, what happens with \begin{foo} is that LaTeX essentially does

\begingroup<bookkeping stuff>\foo

with some bookkeeping for later checks in the \end part. Similarly, \end{foo} will do

\endfoo<checks>\endgroup

Thus the same method used for ordinary macros will work; however,

\let\svfigure\figure
\let\endsvfigure\endfigure
\renewenvironment{figure}
  {\blahblah\begin{svfigure}}
  {\end{svfigure}\moreblah}

wouldn't be a particularly good way to patch the figure environment. For one thing, a typing mistake \end{fiugre} in the input file would result in the puzzling message

! LaTeX Error: \begin{svfigure} on input line 11 ended by \end{fiugre}.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...

l.13 \end{fiugre}

and users will not understand it because they have no idea about the svfigure environment they have never started. The usual solution is to say

\let\svfigure\figure
\let\endsvfigure\endfigure
\renewenvironment{figure}
  {\blahblah\svfigure}
  {\endsvfigure\moreblah}

so that the <bookkeeping stuff> will be done for figure but not for the inner svfigure.

However, not all environments are happily redefined this way.

Environments taking an optional argument should use \LetLtxMacro (with \usepackage{letltxmacro}) instead of \let, but this is true also for macros. For the \end... command \let is always sufficient. More importantly, this way of doing might not work with environments defined with different tools, notably \NewEnviron (environ package) or \NewDocumentEnvironment (xparse package).

Some other environments have their own peculiarities: don't try redefining lrbox (which wouldn't make sense anyway), nor verbatim. Something like

\let\svverbatim\verbatim
\let\endsvverbatim\endverbatim
\renewenvironment{verbatim}
  {<something>\svverbatim}
  {\endsvverbatim<something>}

will simply not work and keep you in permanent verbatim mode unless you load the verbatim package. But don't try this for changing the font size in verbatim, it won't work.

Patching macros and environments is sometimes an arcane activity best suited for necromancers rather than even skilled LaTeX programmers.

1
  • "But don't try this for changing the font size in verbatim, it won't work." Indeed, I know a different trick for accomplishing that. Thanks for the great answer, as always. Commented May 29, 2013 at 18:32
6

If you're using LaTeX 2023-06-01 or later1, the commands \NewEnvironmentCopy, \RenewEnvironmentCopy, and \DeclareEnvironmentCopy are built into the kernel, so you can use:

\NewEnvironmentCopy{<new env>}{<existing env>}

to make a copy. This will work regardless if the environment was defined using \newenvironment or \NewDocumentEnvironment. This method should be preferred over using \let, because it won't fail in case the environment takes optional arguments or things like that.

The same release adds \ShowEnvironment{<env>} to see the definition of an environment on the terminal.

1 Meanwhile you can use the -dev format.

1
1

I did something similar for verb conjugation tables. This is a supplemental answer to Clemenses ;) answer.

  • create an environment for the present tense
  • create a duplicate command (macro)
  • duplication this environment for all other tenses

Why? Later I might want to adjust specific tenses (add coloring or whatever formatting changes), but for the moment I just want to get typing and have a working document.

\documentclass{article}
\usepackage{fontspec}
\newenvironment{present}
{\leavevmode%
\begin{tabular}{ll}}{\\ \end{tabular}}%

\newcommand\duppresent[1]{\expandafter\let\csname #1\endcsname\present\expandafter\let\csname end#1\endcsname\endpresent}

\duppresent{future}% copy environment present to "future"
\duppresent{perfect}% copy environment present to "perfect"

\begin{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{present}
i gea & mir gia(n)\\
du geast & eß geats/ihr geat\\
er/sie/es geat & sie gia(n) 
\end{present}
\end{document}
1

\NewCommandCopy works. However, it has the disadvantage of requiring annoying syntax when e.g. environment name containing * is needed.

You can implement your own \NewEnvironmentCopy as follows (in newer LaTeX kernels \NewEnvironmentCopy is built-in so it may be a better idea to use \ProvideDocumentCommand that will silently fallback to use the existing command if it's already defined):

%! TEX program = lualatex
\documentclass{article}
\usepackage{amsmath}
\begin{document}

\NewDocumentEnvironment{test}{m}{hello #1}{end #1}

\begin{test}{aaa}
    environment body
\end{test}

\ExplSyntaxOn
\ProvideDocumentCommand \NewEnvironmentCopy {mm} {
    \expandafter \NewCommandCopy \csname#1\expandafter\endcsname \csname#2\endcsname
    \expandafter \NewCommandCopy \csname end#1\expandafter\endcsname \csname end#2\endcsname
}

\ProvideDocumentCommand \RenewEnvironmentCopy {mm} {
    \expandafter \RenewCommandCopy \csname#1\expandafter\endcsname \csname#2\endcsname
    \expandafter \RenewCommandCopy \csname end#1\expandafter\endcsname \csname end#2\endcsname
}
\ExplSyntaxOff

\NewEnvironmentCopy{testb}{test}
\RenewEnvironmentCopy{testb}{test}

\NewEnvironmentCopy{oldalign*}{align*}

\begin{testb}{aaa}
    environment body
\end{testb}

\RenewDocumentEnvironment{test}{m}{redefined #1}{stop #1}

\begin{testb}{aaa}
    environment body
\end{testb}

\begin{oldalign*}
    1 &= 1
\end{oldalign*}

\end{document}

Note that if you use \let instead of \NewCommandCopy then the behavior would be different and unexpected (try it yourself).


Alternatively (which may or may not be applicable depends on the specific use case), you can use the hook system in LaTeX:

%! TEX program = lualatex
\documentclass{article}
\usepackage{amsmath}
\begin{document}

\AddToHook{env/align*/begin}{x}
\AddToHook{env/align*/end}{y}

\begin{align*}
    1 &= 1 \\
    2 &= 2
\end{align*}


\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .