14

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.

user12711
  • 2,753
  • 1
    In this answer, I provide a brief introduction to \newcommand, \renewcommand, \providecommand, \newenvironment, \renewenvironment, \provideenvironment and their starred variants. I try to use examples from packages etc. to illustrate some of them and to give some sense of the advantages and limitations of these commands. (But I do not go beyond these commands as that would be too much for one post and you'd never read to the end!) – cfr Jul 09 '15 at 22:30

1 Answers1

18

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.

  • @StevenB.Segletes: Only for the quotation? ;-) –  Jul 09 '15 at 18:51
  • 3
    @StevenB.Segletes: In German language usage, everything (!!!) is a ding :-P –  Jul 09 '15 at 18:59
  • @cfr: Oh come on... only upvoting for the one ... your the second one :-P (of which I know) –  Jul 09 '15 at 22:27
  • +1. Very minor point: You could add a mention of the \AtBeginEnvironment and \AtEndEnvironment macros of the etoolbox package. – Mico Jul 09 '15 at 22:28
  • @Mico: Yes, that's a good comment. How could I forget them? I will add it and as well mention the environ package. –  Jul 09 '15 at 22:29
  • 1
    To be honest, I didn't read the rest: you're competent; I trust you. – cfr Jul 09 '15 at 22:32
  • I just thought some more about my comment and realized that \AtBeginEnvironment won't do the job correctly in the present case, since it inserts its code before the start of the quotation environment. Better to use \apptocmd, which has the same syntax as \xapptocmd. – Mico Jul 09 '15 at 22:35
  • @cfr ;-) ......... (filler) –  Jul 09 '15 at 22:35
  • http://i.stack.imgur.com/46uhg.png – cfr Jul 09 '15 at 22:38
  • @Mico: In some sense this depends on the request. You could use \AfterEndEnvironment and \BeforeBeginEnvironment then too, depending what is really needed –  Jul 09 '15 at 22:38
  • @cfr: Mellon ;-) You will receive another Good Answer badge most likely ;-) –  Jul 09 '15 at 22:40
  • Even with your first solution, I usually prefer to say \quotation … \endquotation, instead of \begin{quotation} … \end{quotation}, in the definition of the foo environment: in this way, if I mispell the name foo in \end{foo}, I get an error about an incorrectly closed foo environment, not about an incorrectly closed quotation environment. This might be important if I hand out the definition file to someone else… (+1, anyway :-) – GuM Jul 09 '15 at 23:28
  • @GustavoMezzetti: Yes, you're right. I sometimes switch between the two possibilities. –  Jul 09 '15 at 23:30
  • I thought I had a typo, but keep getting a "Undefined Control Sequence error" while creating my \newenvironment as follows: "\newenvironment{\MyLetter}{\singlespacing\begin{quotation}\Writinghand}{\ding{47}\end{quotation}}" I'm using koma script's scrbook. Any ideas? – user12711 Jul 09 '15 at 23:31
  • 2
    @user12711: \newenvironment{MyLetter}, not with the backslash. –  Jul 09 '15 at 23:33
  • 1
    @user12711: By the way: marvosym package defines \WritingHand, but uses \let\Writinghand\WritingHand then, so both commands are defined, \Writinghand just being a copy of the other one. –  Jul 09 '15 at 23:46
  • 1
    BINGO (it works!) – user12711 Jul 10 '15 at 00:04
  • The wrapper solution may cause problems later with the customization involving counters or cooperation with other packages such as cref. You do not want to refer to the parent environment, but to the new one. – Dávid Natingga Aug 23 '15 at 08:45
  • @DávidTóth: That's not the question here -- the question was of a general nature, not of counters etc. –  Aug 26 '15 at 08:39