How can I determine whether a custom environment is followed by a new paragraph?

I'm writing a custom command that uses minipages to display its contents:

\newcommand{\example}[1]{\noindent\begin{minipage}{\linewidth}#1\end{minipage}}


The spacing around this command should be similar to that of the align environment: \abovedisplayskip above, and \belowdisplayskip after, so I add \par\vspace{…} before and after:

\newcommand{\example}[1]{%
\par\vspace{\abovedisplayskip}
\noindent\begin{minipage}{\linewidth}#1\end{minipage}


This isn't perfect, though: it adds an extra \parskip before and after the contents, even if there are no blank lines around the command:

AAA % too much space (extra \parskip) after this
\example{\lipsum[1]}
BBB % too much space (extra \parskip) before that


I can remove the first one by changing the definition to this:

\newcommand{\example}[1]{%
\ifvmode\else\par\vspace{-\parskip}\fi\vspace{\abovedisplayskip}
\noindent\begin{minipage}{\linewidth}#1\end{minipage}


But what's the equivalent fix for the bottom of the command? If I use a negative space the \parskip will always be suppressed. Ideally, I'd like text immediately following the command to not be indented, just like with .

Here's a minimal example:

\documentclass{minimal}

\usepackage{lipsum}
\setlength{\parskip}{50pt}

\newcommand{\example}[1]{%
\ifvmode\else\par\vspace{-\parskip}\fi\vspace{\abovedisplayskip}
\noindent\begin{minipage}{\linewidth}#1\end{minipage}

\begin{document}

% There should be small spaces after AAA and before BBB
AAA
\example{\lipsum[1]}
BBB

% There should be a large space after AAA and a small one before BBB
% And BBB shouldn't be indented
AAA

\example{\lipsum[1]}
BBB

% There should be a small space after AAA and a large one before BBB
AAA
\example{\lipsum[1]}

BBB
\end{document}


The standard way to do this would be to define your environment as a trivlist, almost all latex display environments (verbatim, center, quote,..) are defined as trivlists (special one-item version of list) exactly for this reason.

The trivlist code detects whether there is a paragraph break before the list and if so has paragraph spacing before and after, but if there is no paragraph break before the list then the internal \par after the list is hidden and paragraph indentation of the following text is suppressed.

\begin{center}\end{center} for example is defined by

\def\center{\trivlist \centering\item\relax}
\def\endcenter{\endtrivlist}

• I believe this just inhibits paragraph indents, but not skips. If you enter word\begin{center}centred words\end{center}word, then \parskip is still inserted twice. – Circumscribe Nov 5 '18 at 12:04
• @Circumscribe it uses \partopsep as \parskip in the \par case and \topsep as \parskip in the no-\par case, so by setting up those list parameters you can achieve (some) control over that – David Carlisle Nov 5 '18 at 12:15
• @Circumscribe topsep=0pt isn't it? but something like that, yes, this is consistent with all other latex displays so hopefully packages like \parskip which set things up for non zero parskip would automatically do the right thing here. – David Carlisle Nov 5 '18 at 12:47
• Sorry, I had deleted my comment (before you had replied, I thought) because I wasn't sure about it anymore. For me setting \partopsep=\parskip and \topsep=-\parskip works. I think both \parskip and \topsep are always inserted (so this way they cancel out) and \partopsep is only inserted when there is a preceding paragraph break. – Circumscribe Nov 5 '18 at 12:57
• @Circumscribe er yes that sounds about right:-) – David Carlisle Nov 5 '18 at 13:02

Here is a possible solution. I'm defining the macro \tentativepar which inserts \par and sets both \parskip and \parindent to 0pt. It moreover temporarily redefines \par and \everypar (which is inserted at the start of every paragraph) to reset the original values (and themselves).

The net effect is that \tentativepar will insert a paragraph break without any skip or indentation unless it is followed by another \par.

Here is an example:

\documentclass{article}

\setlength{\parskip}{50pt}

%% vv We need to store a couple of values
\newskip\savedparskip
\newskip\savedparindent
\newtoks\savedeverypar

\makeatletter
\newcommand*\tentativepar{%
\par
\savedparskip\parskip         %% <- store original parskip
\savedparindent\parindent     %% <- store original parindent
\expandafter\savedeverypar\expandafter{\the\everypar}
%% ^^ store original \everypar
\parindent0pt                 %% <- set parindent to 0pt
%% vv restore everything that was saved after the first \par
\def\par{%
\@restorepar                  %% <- restore original \par
\expandafter\everypar\expandafter{\the\savedeverypar}%
\parskip\savedparskip         %% <- reset parskip
\parindent\savedparindent     %% <- reset parindent
\par                          %% still insert a paragraph break
}%
%% vv or at the stat of the next paragraph
\expandafter\everypar\expandafter{%
\the\savedeverypar
\@restorepar                  %% <- restore original \par
\expandafter\everypar\expandafter{\the\savedeverypar}%
\parskip\savedparskip         %% <- reset parskip
\parindent\savedparindent     %% <- reset parindent
}
}
\makeatother

\newcommand{\example}[1]{%
\ifvmode\else\tentativepar\fi
\vspace{\abovedisplayskip}
\noindent\begin{minipage}{.5\linewidth}#1\end{minipage}%
\vspace{\belowdisplayskip}
\tentativepar
}

\begin{document}

AAA
\example{There should be small spaces after AAA and before BBB}
BBB

AAA

\example{There should be a large space after AAA and a small one before BBB. And BBB shouldn't be indented.}
BBB

AAA
\example{There should be a small space after AAA and a large one before BBB}

BBB

CCC

\end{document}