[Tex/LaTex] LaTeX programming Inheritance with \newcommand \newenvironment \renew… etc

environmentsmacros

I'd like to create (or renew) several LaTeX commands and environments, and understand how it works. One of my new environments would be equivalent to \begin{quotation}. So, imagine my \begin{foo} environment. I want it to inherit all the functionality of quotation environments, but would like to add a \Writinghand glyph and revert to \singlespacing. Then at the end of the quotation text I will place a \ding{47} glyph at the end of the last sentence, plus revert back to my usual \onehalfspacing (that's actually one plus a half line spacing) after the \end{foo} is issued.

How would that be written? And, explain how to get \renewenvironment and \renewcommand to inherit all the functionality of the original command, because I understand "\renew..." basically erases the LaTeX macro, and you have to start from the beginning to add back the original functionality. But in all my cases I just want to add extra formatting glyphs and text formatting to arguments, linespacing options for my environments.

Best Answer

The fellowship of \newenvironment and \renewenvironment commands

Yes, \renew... will erase or redefine the previous definition (or eject an error message, if this has not been defined before.)

In many cases it's more suitable to make a wrapper environment, which does not attack the original environment.

\newenvironment{foo}{%
%startup code, i.e the \WritingHand and `\singlespacing`

\begin{quotation}
}{%
% end code
\ding{...}
\end{quotation}
}

Since environments use groups, the change of the line spacing within of foo is safe outside, there is no need to explicitly switch back to \onehalfspacing. This is true for all lengths/skips and colour settings.

\documentclass{article}

\usepackage{setspace}
\usepackage{pifont}
\usepackage{marvosym}
\onehalfspacing
\newenvironment{foo}{%
\singlespacing
\begin{quotation}
\WritingHand

}{%
\ding{47}
\end{quotation}
}

\begin{document}
% Compare the spacing outside and inside of foo environment 

Three Rings for the Elven-kings under the sky,

Seven for the Dwarf-lords in their halls of stone,

Nine for Mortal Men doomed to die,

One for the Dark Lord on his dark throne

In the Land of Mordor where the Shadows lie.

One Ring to rule them all, One Ring to find them,

One Ring to bring them all and in the darkness bind them

In the Land of Mordor where the Shadows lie.


\begin{foo}
Three Rings for the Elven-kings under the sky,

Seven for the Dwarf-lords in their halls of stone,

Nine for Mortal Men doomed to die,

One for the Dark Lord on his dark throne 

In the Land of Mordor where the Shadows lie. 

One Ring to rule them all, One Ring to find them, 

One Ring to bring them all and in the darkness bind them 

In the Land of Mordor where the Shadows lie.
\end{foo}

\end{document}

enter image description here

The renewenvironment version

renewenvironment is basically similar, but one has to keep in mind too design issues:

  • Should the environment be extended/improved --> use the old definition
  • Completely drop the old behaviour --> do not use the old definition (of course)

I assume that the old behaviour should be available still, so I tried the first approach:

  1. First store the old environment startup code, which is just the command \quotation, to something, say \latex@quotation.
  2. Store the environment end code, which is \endquotation then, to \latex@endquotation.
  3. Basically proceed as in the \newenvironment version and replace \begin{quotation} with \latex@quotation and \end{quotation} with \latex@endquotation.

The output is the same.

Important note: If the environment has optional arguments, it's better to use \LetLtxMacro instead of \let, you need the letltxmacro package then.


\documentclass{article}

\usepackage{setspace}
\usepackage{pifont}
\usepackage{marvosym}
\onehalfspacing % for this document ....

\makeatletter
\let\latex@quotation\quotation
\let\latex@endquotation\endquotation

\renewenvironment{quotation}{%
\singlespacing
\latex@quotation
\WritingHand

}{%
\ding{47}
\latex@endquotation%
}
\makeatletter

\begin{document}
% Compare the spacing outside and inside of foo environment 

Three Rings for the Elven-kings under the sky,

Seven for the Dwarf-lords in their halls of stone,

Nine for Mortal Men doomed to die,

One for the Dark Lord on his dark throne

In the Land of Mordor where the Shadows lie.

One Ring to rule them all, One Ring to find them,

One Ring to bring them all and in the darkness bind them

In the Land of Mordor where the Shadows lie.


\begin{quotation}
Three Rings for the Elven-kings under the sky,

Seven for the Dwarf-lords in their halls of stone,

Nine for Mortal Men doomed to die,

One for the Dark Lord on his dark throne

In the Land of Mordor where the Shadows lie.

One Ring to rule them all, One Ring to find them,

One Ring to bring them all and in the darkness bind them

In the Land of Mordor where the Shadows lie.
\end{quotation}

% And once again

Three Rings for the Elven-kings under the sky,

Seven for the Dwarf-lords in their halls of stone,

Nine for Mortal Men doomed to die,

One for the Dark Lord on his dark throne

In the Land of Mordor where the Shadows lie.

One Ring to rule them all, One Ring to find them,

One Ring to bring them all and in the darkness bind them

In the Land of Mordor where the Shadows lie.



\end{document}

Edit The shortest version: Use the xpatch command and its \xpretocmd and \xapptocmd macros.

\documentclass{article}

\usepackage{setspace}
\usepackage{pifont}
\usepackage{marvosym}
\usepackage{xpatch}
\onehalfspacing % for this document ....

\newcommand{\lotrtext}{%
Three Rings for the Elven-kings under the sky,

Seven for the Dwarf-lords in their halls of stone,

Nine for Mortal Men doomed to die,

One for the Dark Lord on his dark throne

In the Land of Mordor where the Shadows lie.

One Ring to rule them all, One Ring to find them,

One Ring to bring them all and in the darkness bind them

In the Land of Mordor where the Shadows lie.
}

% Append the `\singlespacing` etc. after the `\quotation` startup is called
\xapptocmd{\quotation}{\singlespacing%
\WritingHand%
}{}{}
% Prepend `\ding{47}` before `\endquotation` comes into action. 
\xpretocmd{\endquotation}{\ding{47}}{}{}

\begin{document}
% Compare the spacing outside and inside of foo environment 

\lotrtext


\begin{quotation}
\lotrtext
\end{quotation}

% And once again

\lotrtext
\end{document}

And the final (?) remark -- your possibilities:

  • Refine a wrapper with \newenvironment
  • Change or completely erase the previous definition with \renewenvironment
  • Patch the commands with \xpretocmd, \xapptocmd or \xpatchcmd which is something between the 1st and 2nd. way.

All depends on the requested design, there is no general rule. (And if redefinition or new definition, I suggest to use \NewDocumentEnvironment or \RenewDocumentEnvironment from the very powerful xparse package.

Update

Mico made an important comment:

The package etoolbox provides the commands \AtBeginEnvironment and \AtEndEnvironment, \BeforeBeginEnvironment and \AfterEndEnvironment which do basically the same what I've done with \xpretocmd and \xapptocmd or the explicit \renewenvironment, however, to an existing environment only -- which of the etoolbox commands should be used depends on the precise requirements, what should be done before or after the group begins/ends.

As an alternative, there is NewEnviron from the environ package, which allows for the saving of the environment body into \Body macro.

Related Question