Eine LaTeX-Umgebung ist überhaupt kein besonderes Konstrukt. \begin{quote} ruft einen Befehl namens \quote auf, \end{quote} einen namens \endquote – mehr steckt nicht dahinter. \newenvironment, das Werkzeug für eigene Umgebungen, schreibt schlicht diese beiden Makros in einem Zug. Ist dieses Paar einmal verstanden, ergibt sich der Rest von selbst: warum ein Umgebungsname mit einem Befehlsnamen kollidieren kann, warum die Argumente im Endcode fehlen und warum ein nicht passendes \begin/\end genau jene Fehlermeldung erzeugt. Diese Seite verfolgt den Faden durch \newenvironment und \renewenvironment, Argumente und optionale Argumente, die kostenlose Gruppierung bis hin zum modernen \NewDocumentEnvironment.
\newenvironment schreiben: Anfangscode und Endcode
Steht \newenvironment{name}{Anfangscode}{Endcode} in der Präambel, lässt sich im Text \begin{name}…\end{name} verwenden. Das erste Argument ist der Umgebungsname, ohne Backslash geschrieben; das zweite ist der Code, der bei \begin{name} läuft, das dritte der Code, der bei \end{name} läuft. Der dazwischen eingeschlossene Text bleibt unangetastet und wird ganz normal gesetzt. Der Entwurf einer eigenen Umgebung läuft damit auf eine einzige Frage hinaus: Was wird beim Eintreten vorbereitet, und was beim Verlassen wieder aufgeräumt?
% preamble: define a warning environment
\newenvironment{warning}{%
\par\noindent\textbf{Warning:}\itshape
}{%
\par
}
% body: use it
\begin{warning}
This operation cannot be undone.
\end{warning}Dass der Endcode in diesem Beispiel nur aus einem \par besteht, ist keine Nachlässigkeit. Nirgends wird das im Anfangscode eingeschaltete \itshape (Kursive) zurückgenommen, und trotzdem steht der Text nach der Umgebung wieder aufrecht. Die beiden nächsten Abschnitte erklären das ausführlich; kurz gesagt bildet eine Umgebung automatisch eine Gruppe. Der Kniff beim Schreiben von \newenvironment besteht darin, nichts zurückzusetzen, was sich von selbst zurücksetzt – ein leeres {} ist ein völlig brauchbarer Endcode.
Was eine Umgebung wirklich ist: das Makropaar \name und \endname
\begin{name} ruft \name auf, \end{name} ruft \endname auf. Das ist keine Vermutung: Die Definition von \end in latex.ltx verwendet wörtlich \csname end#1\endcsname. Wer es lieber sehen als glauben möchte, greift zu \show. Für die Standardumgebung quote liefern \show\quote und \show\endquote ein Makro, das eine \list öffnet, und eines, das schlicht \endlist lautet. Eine eigene Umgebungs-Syntax gibt es nirgends – nur zwei über den Namen verkoppelte Makros.
% ask LaTeX what the quote environment is actually made of
\show\quote
% > \quote=\long macro:
% -> \list {}{\rightmargin \leftmargin }\item \relax .
\show\endquote
% > \endquote=\long macro:
% -> \endlist .
% so \begin{quote} ... \end{quote} is, in effect:
% \begingroup \quote ... \endquote \endgroupDie Folge davon begegnet einem fast sofort, und zwar als Fehlermeldung. Ein Umgebungsname belegt den gleichlautenden Befehlsnamen. Ein Versuch mit \newenvironment{alpha}{...}{...} endet mit ! LaTeX Error: Command \alpha already defined. – denn den griechischen Buchstaben \alpha gibt es bereits. Umgebungs- und Befehlsnamen waren nie zwei Namensräume, sondern immer nur einer. \newenvironment{quote} scheitert aus demselben Grund an Command \quote already defined., und diese Prüfung ist Absicht, denn sie verhindert versehentliches Überschreiben. Namen wie mywarning oder thmbox, die kaum mit vorhandenen Befehlen kollidieren, sind die sichere Gewohnheit.
Die Kehrseite derselben Logik ist eine bekannte Einschränkung auf der Seite von \newcommand: \newcommand{\endnotes}{...} wird abgelehnt, obwohl es einen solchen Befehl nirgends gibt. Dürfte jeder Namen mit end am Anfang erfinden, könnten sie mit der \endname-Hälfte kollidieren, die \end{...} aufruft; deshalb ist das ganze Präfix reserviert. Die Einzelheiten dieser Prüfung stehen auf der Makro-Seite. Entscheidend ist hier: Das Verbot dort schützt genau den Namensraum, aus dem die Umgebungen dieser Seite gebaut sind.
Umgebungen sind automatische Gruppen: was zurückgesetzt wird und was entkommt
Vor dem Ausführen des Anfangscodes setzt \begin ein \begingroup, nach dem Endcode setzt \end das passende \endgroup. Anfangscode, Text und Endcode liegen damit alle in ein und derselben Gruppe. Genau deshalb musste die obige Umgebung warning das \itshape nie zurücknehmen. Bei einem Makro wäre der Inhalt selbst in { … } einzuschließen; in einer Umgebung ist \begin…\end genau dieses Klammerpaar. Soll eine Formatänderung auf einen festen Bereich beschränkt bleiben, ist eine Umgebung ehrlicher als ein Makro – und darin liegt der ganze Grund.
Das heißt jedoch nicht, dass am Gruppenende alles zurückgesetzt würde. TeX kennt lokale und globale Zuweisungen, und LaTeX hält Zähleroperationen absichtlich global: \addtocounter ist in latex.ltx mit \global\advance geschrieben, sodass ein innerhalb einer Umgebung per \stepcounter erhöhter Zähler auch nach \end erhöht bleibt. Diesem Entwurf verdankt es sich, dass eine in einer Umgebung weitergezählte Abschnitts- oder Abbildungsnummer nicht verschwindet. Ein mit \newcommand innerhalb der Umgebung definiertes Makro stirbt dagegen mit \end; außerhalb verwendet, ergibt es ! Undefined control sequence.
| Innerhalb der Umgebung ausgeführt | Nach \end | Warum |
|---|---|---|
\itshape | wird zurückgesetzt | ein Schriftwechsel ist eine lokale Zuweisung |
\setlength | wird zurückgesetzt | \setlength ist eine einfache, lokale Zuweisung |
\newcommand | verschwindet | die Definition ist lokal; außerhalb folgt ! Undefined control sequence. |
\stepcounter | bleibt erhalten | Zähleroperationen sind mit \global geschrieben |
\gdef | bleibt erhalten | eine ausdrücklich globale Definition |
\label | bleibt erhalten | ein Schreibvorgang in die .aux-Datei wird von einer Gruppe nicht rückgängig gemacht |
Umgebungen mit Argumenten und ein optionales Argument mit Standardwert
Damit sich der Inhalt von Aufruf zu Aufruf ändert, wird die Argumentzahl in eckigen Klammern hinter den Namen geschrieben und im Anfangscode mit #1, #2 und so weiter darauf zugegriffen: \newenvironment{name}[⟨n⟩]{Anfangscode}{Endcode}, von #1 bis #9, höchstens neun. Ein zweites Klammerpaar – \newenvironment{name}[⟨n⟩][⟨Standard⟩]{...}{...} – macht #1 optional: \begin{name} nimmt den Standardwert, \begin{name}[x] setzt x in #1. Die Schreibweise gleicht \newcommand bis hin zur Regel, dass ⟨n⟩ die Gesamtzahl einschließlich des optionalen Arguments ist.
% one mandatory argument
\newenvironment{point}[1]{%
\par\noindent\textbf{#1}\quad
}{%
\par
}
\begin{point}{Conclusion}
Back up early.
\end{point}
% first argument optional, default "Note"
\newenvironment{callout}[1][Note]{%
\par\noindent\textbf{#1:}\itshape
}{%
\par
}
\begin{callout} % label is "Note"
Nothing to configure.
\end{callout}
\begin{callout}[Warning] % #1 becomes "Warning"
This cannot be undone.
\end{callout}Warum #1 im Endcode einen Fehler ergibt und wie man es umgeht
Die Argumente #1, #2, … lassen sich nur im Anfangscode verwenden. Steht eines im Endcode, beschwert sich LaTeX nicht erst bei der Benutzung, sondern schon in der definierenden Zeile: ! Illegal parameter number in definition of \enddemo. Bemerkenswert ist der Name, den der Fehler nennt: \enddemo. Er bestätigt alles Bisherige, denn \newenvironment erzeugt ein Makro \demo, das Argumente entgegennimmt, und ein Makro \enddemo, das überhaupt keine entgegennimmt. Ein #1 im Rumpf eines Makros ohne Parametertext ist für TeX schlicht ein Syntaxfehler. Die Argumente verschwinden nicht zur Laufzeit; es gab nie eine Stelle, die sie aufnehmen könnte.
Wird der Argumentwert am Schluss tatsächlich gebraucht, lautet der übliche Griff: den Wert noch im Anfangscode sichern. Für Text ist eine Box die robuste Wahl – mit \newsavebox reservieren, mit \sbox füllen. Ebenso kann sich ein Makro den Wert über \def oder \newcommand merken. Da die ganze Umgebung eine Gruppe bildet, übersteht das im Anfangscode Gespeicherte den Weg bis zum Endcode unversehrt. Das folgende citequote setzt am Ende eines Zitats die Quelle rechtsbündig: Es nimmt die Quelle als #1 entgegen (Standard Shakespeare), legt sie in die Box \quoteauthor und holt sie im Endcode mit \usebox wieder hervor.
\newsavebox{\quoteauthor}
\newenvironment{citequote}[1][Shakespeare]{%
\sbox\quoteauthor{#1}% save the argument while we still have it
\begin{quotation}%
}{%
\hspace{1em plus 1fill}---\usebox{\quoteauthor}% retrieve it here
\end{quotation}%
}
\begin{citequote}
To be, or not to be.
\end{citequote}
\begin{citequote}[Knuth]
Premature optimization is the root of all evil.
\end{citequote}\renewenvironment und die Sternform \newenvironment*
Um eine bereits vorhandene Umgebung umzubauen, dient \renewenvironment. Die Argumentsyntax, [⟨n⟩][⟨Standard⟩] eingeschlossen, gleicht Wort für Wort der von \newenvironment; nur die Voraussetzung unterscheidet sich. Während \newenvironment nur bei freiem Namen gelingt, gelingt \renewenvironment nur bei belegtem Namen; auf einen nicht existierenden Namen angewandt, endet es mit ! LaTeX Error: Environment nosuch undefined. Das eignet sich für flächendeckende Änderungen, etwa jedes quote eines Dokuments kursiv zu setzen. Zu bedenken bleibt, dass das Umdefinieren einer von Klasse oder Paket gelieferten Umgebung fremden Code mitreißen kann, der darauf aufbaut.
% italicise every quote in the document
\renewenvironment{quote}{%
\list{}{\rightmargin\leftmargin}\item\relax\itshape
}{%
\endlist
}Sowohl \newenvironment als auch \renewenvironment besitzen eine Sternform mit * nach dem Namen. Der Stern entscheidet darüber, ob ein Argument eine Leerzeile enthalten darf. Argumente ohne Stern dürfen Absätze (\par) überspannen; mit Stern werden sie zu kurzen Argumenten, und eine Leerzeile darin beendet den Lauf mit ! Paragraph ended before \shortenv was complete. Das klingt nach Einschränkung, ist aber eine Wohltat: Läuft ein Argument wegen einer vergessenen schließenden Klammer davon, hält es dank Stern an der nächsten Leerzeile statt erst am Dokumentende. Ein Standard-Gegenstück zu \providecommand, also „nur definieren, falls nicht vorhanden“, gibt es für Umgebungen übrigens nicht; dafür dient das Werkzeug des nächsten Abschnitts.
\NewDocumentEnvironment: Argumente, die auch der Endcode sieht
Mit \NewDocumentEnvironment{name}{⟨arg-spec⟩}{Anfangscode}{Endcode} erübrigt sich der Umweg über die gespeicherte Box, denn in dieser Schnittstelle greifen beide Hälften auf dieselben Argumente zu. Statt einer Anzahl wird eine Argumentspezifikation notiert: Buchstaben wie m (obligatorisch), o (optional), O{Standard} (optional mit Standardwert) und s (optionaler Stern). Ursprünglich stammt das aus dem Paket xparse, doch die Ausgabe vom 1. Oktober 2020 hat es in den LaTeX-Kernel übernommen, sodass es heute ohne \usepackage bereitsteht (die Liste der Spezifizierer steht auf der xparse-Seite).
% O{...} is an optional argument with a default; #1 works in both halves
\NewDocumentEnvironment{citequote}{O{Shakespeare}}{%
\begin{quotation}%
}{%
\hspace{1em plus 1fill}---#1%
\end{quotation}%
}
\begin{citequote}[Knuth]
Premature optimization is the root of all evil.
\end{citequote}Dieselbe Ausgabe brachte auch \RenewDocumentEnvironment (umbauen), \ProvideDocumentEnvironment (nur definieren, falls nicht vorhanden) und \DeclareDocumentEnvironment (in jedem Fall definieren). Das im vorigen Abschnitt vermisste Umgebungs-Gegenstück zu \providecommand steht damit endlich bereit. Für neuen Code verdient diese Familie aus drei Gründen den Vorzug: mehrere optionale Argumente, saubere Behandlung von Sternvarianten und ein Endcode, der die Argumente sieht. \newenvironment bleibt vor allem für das Lesen und Pflegen bestehender Dokumente im Gedächtnis.
Drei Muster, die die meisten eigenen Umgebungen abdecken
Fast jede in der Praxis geschriebene Umgebung fällt in eines von drei Mustern. Das Gerüst ist überall gleich: im Anfangscode vorbereiten, im Endcode aufräumen.
- Den Text in Formatierung hüllen – im Anfangscode Schrift, Größe oder Ausrichtung setzen und den Text dieses Aussehen annehmen lassen. Dank der Gruppierung darf der Endcode leer bleiben.
- Abstand oben und unten einfügen – vertikalen Abstand wie
\par\medskipan den Anfang des Anfangscodes und an das Ende des Endcodes setzen, sodass der Text oben und unten gerahmt wird. - Auf einer vorhandenen Umgebung aufbauen – im Anfangscode eine andere Umgebung mit
\begin{...}öffnen und im Endcode das passende\end{...}schließen. So würzt manquote,centeroderlistmit etwas Zusätzlichem.
% 1. wrap the body in formatting
\newenvironment{aside}{\par\small\itshape}{\par}
% 2. add vertical space above and below
\newenvironment{spaced}{\par\medskip\noindent}{\par\medskip}
% 3. build on an existing environment
\newenvironment{smallquote}{%
\small\begin{quotation}%
}{%
\end{quotation}%
}Das dritte Muster kommt am häufigsten zum Einsatz. Die zugrunde liegende Umgebung behält Einzug und Ränder, sodass erstaunlich wenig hinzuzufügen bleibt. Wichtig ist nur, im Endcode alles wieder zu schließen, was der Anfangscode geöffnet hat, damit die Paarung intakt bleibt. Und die % am Zeilenende in den Beispielen oben sind kein Zierrat: Ohne sie gerät der Zeilenumbruch als einzelnes Leerzeichen in den Text, und um die Umgebung herum entstehen unerklärliche Lücken. Wer sich das abschließende % bei mehrzeiligen Definitionen angewöhnt, erspart sich eine ganze Klasse von Pannen.
Fehler, wenn \begin und \end nicht zusammenpassen
\end{name} ruft nicht nur \endname auf, sondern prüft auch, ob name zur gerade geöffneten Umgebung passt. Stimmt beides nicht überein, erscheint etwa ! LaTeX Error: \begin{sidenote} on input line 5 ended by \end{remarkbox}. – gemeldet werden der Name der geöffneten Umgebung und die Zeile, in der sie geöffnet wurde. Diese Zeilennummer hilft meist mehr als die Zeile, in der der Fehler auftaucht. Vier Meldungen begegnen einem in der Praxis immer wieder.
! LaTeX Error: Environment nosuchenv undefined.– eine Umgebung dieses Namens existiert nicht. Entweder ein Tippfehler oder ein vergessenes\usepackagefür das definierende Paket.! LaTeX Error: \begin{sidenote} on input line 5 ended by \end{remarkbox}.– geöffneter und geschlossener Name stimmen nicht überein.! LaTeX Error: \begin{sidenote} on input line 4 ended by \end{document}.– ein\end{sidenote}fehlt, die Umgebung blieb bis\end{document}offen.! LaTeX Error: \begin{document} ended by \end{nosuchenv}.– geschlossen wurde eine nie geöffnete Umgebung. Diese Meldung folgt auch direkt auf ein undefiniertes\begin, das bereits einen eigenen Fehler ausgelöst hat.
Alle vier formulieren dasselbe Symptom: Etwas ist nicht gepaart. Öffnet die eigene Umgebung im Anfangscode eine weitere Umgebung, gehört der Verdacht zuerst dem \end{...} im Endcode. Zu beachten ist außerdem, dass solche Fehler häufig scheinbar unbeteiligte Aufschreie wie ! Missing $ inserted. nach sich ziehen. Der Folgeschaden fällt lauter aus als die Ursache, deshalb gilt: die Logdatei ab dem allerersten Fehler lesen.