I have a document class test.cls, in which I have defined a custom environment myenvironment using the environ package as follows:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{test}
\LoadClass{article}
\RequirePackage{changepage}
\RequirePackage{environ}
\NewEnviron{myenvironment}{
    \begin{adjustwidth}{10pt}{10pt}
        \BODY
    \end{adjustwidth}
}

Now if I try to use tikz-cd inside myenvironment, it fails, e.g. this gives an error:

\begin{myenvironment}
    \[\begin{tikzcd}
        1 \ar[r] \ar[d] & 2 \ar[d] \\
        3 \ar[r] & 4 \\
    \end{tikzcd}\]
\end{myenvironment}

On the other hand, if I simply write it like this—that is, I substitute what the environment would do—, everything is fine:

\begin{adjustwidth}{10pt}{10pt}
    \[\begin{tikzcd}
        1 \ar[r] \ar[d] & 2 \ar[d] \\
        3 \ar[r] & 4 \\
    \end{tikzcd}\]
\end{adjustwidth}

What could be the problem?

  • 2
    Why do you have to collect the contents of myenvironment? Isn't LaTeX's \newenvironment is enough here? – Phelype Oleinik Oct 22 at 20:50
  • Indeed it is enough, I guess I just got too used to the environ package to notice. I’ve tried it now and it solves the problem. I still wonder why it doesn’t work with environ though. – Hermetically Sealed Halibut Oct 22 at 20:55
  • I posted an answer explaining why it doesn't work :) – Phelype Oleinik Oct 22 at 21:14
up vote 2 down vote accepted

You have to be very careful when collecting an environment with environ, as this usually brings problems. When environ collects its the environment it "freezes" the tokens of the environment. That is, everything will mean what it was when the outer environment was called. This will break every environment that somewhere redefines the category codes of any character (verbatim environments, for example), or that redefine some character.

In this particular case, the meaning of the & changes when you enter tikzcd. See:

\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz-cd}
\begin{document}

When \texttt{environ} collects the environment:

{\ttfamily\meaning&}

\bigskip

What \texttt{tikzcd} expects \texttt{\&} to be:

\begin{tikzcd}
\vbox to \textwidth{\ttfamily\meaning&}
\end{tikzcd}
\end{document}

enter image description here

So basically, inside tikzcd, & means one thing, but when environ reads it, it freezes the alignment tab character & meaning, so it won't work.

So use environ only when you are sure that you need it and you are sure that it won't break anything.

In your case you don't need environ at all because you aren't applying any operation to the collected contents, so this:

\newenvironment{myenvironment}
  {%
    \begin{adjustwidth}{10pt}{10pt}%
  }%
  {%
    \end{adjustwidth}
  }

will suffice.

It doesn't work because myenvironment absorbs the body of the environment as a macro argument; in these cases you have to pass ampersand replacement=\& to tikzcd and use \& instead of &, so

\begin{myenvironment}
    \[\begin{tikzcd}[ampersand replacement=\&
        1 \ar[r] \ar[d] \& 2 \ar[d] \\
        3 \ar[r] \& 4 \\
    \end{tikzcd}\]
\end{myenvironment}

However, there's no need for environ here:

\newenvironment{myenvironment}
  {\begin{adjustwidth}{10pt}{10pt}}
  {\end{adjustwidth}}

will do as well. And better, actually.

The cargo-cult version would be

\newenvironment{myenvironment}
  {\adjustwidth{10pt}{10pt}}
  {\endadjustwidth}

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.