8

An environment, singlepar, is supposed to contain a single paragraph. This means that there must be no paragraph breaks inside the code it wraps:

\begin{singlepar}
    Right.
\end{singlepar}

\begin{singlepar}
    Wrong.

    Wrong!
\end{singlepar}

My current approach is:

% \usepackage{xparse}
\NewDocumentEnvironment{singlepar}{}
{%
    % Ensure that this environment starts a new paragraph
    \par%
    % Save normal \par for later use
    \let\oldparforprivateuse\par%
    % Use of \par should abort the typesetting process
    \def\par{\typeout{DISALLOWED}\QUIT}%
    \ignorespaces%
}
{%
    % Issue a paragraph break
    \oldparforprivateuse%
}

However, it doesn't feel right. As for actual reasons, I have a feeling that this hack doesn't handle all cases. I believe that there could still be paragraph breaks; for example, by using \let to alias \par's definition before the environment:

\let\takethis\par
\begin{singlepar}
    I'm gonna stab you in the back.
    \takethis
    Take this!
\end{singlepar}

I have checked the xparse documentation and there doesn't seem to be a way to inhibit paragraph breaks as is possible with macro arguments (paragraph breaks are disallowed in macro arguments unless they are preceded by a + in the macro declaration).

How can paragraph breaks be totally inhibited in a section of the code?

EDIT: By "inhibit", I mean that they abort the compilation so that one notices it. This is more about diagnosing my own mistakes when entering text.

  • 1
    You can't inhibit a primitive; so if there is \endgraf in the environment, you'd get a paragraph break. Similarly, any control sequence \let to the primitive \par will do the same. There's no way to know what control sequence names point to the \par primitive. Note that declaring a macro as non \long only disallows the token \par in its argument, but any control sequence \let to the primitive \par will be accepted. – egreg Nov 2 '15 at 22:28
  • 1
    @egreg -- but can't \endgraf be temporarily and locally redefined to trigger a warning, if not an error? (have to be done with great care, of course.) – barbara beeton Nov 2 '15 at 22:31
  • 1
    @barbarabeeton Yes, but one should disable also all other control sequences that have been \let to \par and this information is not available. – egreg Nov 2 '15 at 22:44
5

I don't think you want to hook in to \par that is the end of the first paragraph which really you want to allow, for a one para environment it is natural to allow

\begin{singlepar}

aaa

\end{singlepar}

which would not be allowed if you make \par an error.

What you don't want to allow is a second paragraph to start and that's \everypar There are still some concerns about things (re) defining \everypar but this will catch many cases, and allow the above example.

\documentclass{article}

%\newenvironment{singlepar}
%{\def\par{\PackageError{pkg}{par DISALLOWED}{}\ignorespaces}}%
%{}


\newenvironment{singlepar}
{\everypar{\everypar{\PackageError{pkg}{par DISALLOWED}{}}}\ignorespaces}%
{}
\begin{document}

\begin{singlepar}

aaa

\end{singlepar}


\begin{singlepar}

aaa

b
\end{singlepar}

\end{document}
7

Your feelings are right. With

\begin{singlepar}
Wrong.\endgraf Wrong.
\end{singlepar}

you'd get a paragraph break and no error. The LaTeX format does

\let\endgraf\par

so making \endgraf equivalent to the primitive \par. Also \@@par is equivalent to the primitive \par, but it shouldn't bother you because it has @ in its name.

You could say

\makeatletter
\newenvironment{singlepar}
 {%
  % Ensure that this environment starts a new paragraph
  \@@par
  % Save normal \par for later use
  % Use of \par should abort the typesetting process
  \def\par{\typeout{DISALLOWED}\QUIT}%
  \let\endgraf\par
 }
 {%
  % Issue a paragraph break
  \@@par
 }
\makeatother

but it wouldn't solve the problem, because some package could say \let\foo\@@par and then \foo in the environment would cause a paragraph break.

Since it's not possible to know what control sequences point to the primitive \par memory location for their meaning (like \endgraf and \@@par), the problem is not really solvable.

One could think to absorb the environment's body with a variant of \NewEnviron that makes a non \long macro (the standard action is to do like \newcommand), but this wouldn't solve the problem either. A non \long macro only disallows the token \par in its argument, but will happily accept \endgraf or any control sequence made equivalent to the primitive \par.

Finally, it's not possible to disable a primitive. You can hide it by redefining it, but control sequences already \let to it will continue to work.

  • I'm accepting David Carlisle's answer because it's "more powerful" at addressing the problem (besides making a good point: it's another paragraph starting that I need to disallow), but I feel sorry. Yours is excellent and your reasoning explains all of the caveats. Thank you! :) – Kalrish Nov 5 '15 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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