1

I notice that the behaviour of environments with an optional argument that use \obeylines changes based on whether the optional argument is present or not.

I would have expected all the environments in the following example to look the same. But one is different.

What's going on? Is there something I can add to the definition of bbb to make it behave like all the others when an optional argument is present?

\documentclass{article}
\NewDocumentEnvironment{aaa}{}{\obeylines ENV:}{}
\NewDocumentEnvironment{bbb}{o}{\obeylines ENV:}{}
\NewDocumentEnvironment{ccc}{o}{ENV:}{}
\begin{document}
\begin{aaa}
  x
  y
\end{aaa}
\begin{bbb}
  x
  y
\end{bbb}
\begin{bbb}[arg]
  x
  y
\end{bbb}
\begin{ccc}\par
  x\par
  y\par
\end{ccc}
\begin{ccc}[arg]\par
  x\par
  y\par
\end{ccc}
\end{document}

Output

output

David Purton
  • 25,884

1 Answers1

1

The issue is "obviously" (*) that the optional argument grabber tries to tokenize the newline token, which normally has catcode 5 ("end of line") before \obeylines is executed which changes its catcode to 13 (active).

Since it's required to tokenize the following token to determine whether or not there is an optional argument, it's unavoidable.

(in theory it's possible to use scantokens to "untokenize" it. In practice, it has a performance hit and without setting \everyeof user might hit even more perplexing bugs & maybe confusing \errorcontextlines as well)

For a workaround, set the catcode of the \endlinechar before parsing the optional argument. The cleanest way to do that at the moment (that I know of) is to define auxiliary macro/environment.

%! TEX program = lualatex

\documentclass{article} \NewDocumentEnvironment{aaa}{}{\obeylines ENV:}{} \NewDocumentEnvironment{innerbbb}{o}{ENV:}{} \NewDocumentEnvironment{bbb}{}{\obeylines\innerbbb}{\endinnerbbb} \NewDocumentEnvironment{ccc}{o}{ENV:}{} \begin{document} \begin{aaa} x y \end{aaa} \begin{bbb} x y \end{bbb} \begin{bbb}[arg] x y \end{bbb} \begin{ccc}\par x\par y\par \end{ccc} \begin{ccc}[arg]\par x\par y\par \end{ccc} \end{document}

Side note, use the plain form \innerbbb because Defining environments based on other ones: What's the right way?

Side note 2, this will make the error messages a bit confusing for runaway optional argument (i.e. you forgot to put a ]), reporting the environment name being innerbbb instead of bbb

! File ended while scanning use of \environment innerbbb .

(*) if you're sufficiently experienced in TeX programming

user202729
  • 7,143
  • (originally I made the mistake that it's not necessary to tokenize the following token, fixed now) – user202729 Jun 29 '22 at 15:22
  • Actually there are "argument processors" as well, maybe there's something interesting there. – user202729 Jun 29 '22 at 15:23
  • Thinking about it, the xparse code could as well use the runtime value of @currenvir to grab the argument, but there's a small performance hit there I think. – user202729 Jun 29 '22 at 15:26
  • An alternative workaround is to add an empty mandatory argument at the end, although that makes the source code/input not as pretty.. – user202729 Jul 08 '22 at 11:13