10

I would like to define an environment which behaves differently when it is nested. This environment should ignore spaces at the begin and end of the environment. I have this working as I desire when it is not nested. But, when it is nested I can't seem to get the desired behavior.

The first box here is the desired behavior using a non nested version. I would like the second nested version of this environment to behave as if it were not nested. However, there are two issues with the current implementation:

  1. I am required to add a trailing % to \begin{MyBoxedEnvironment}% when nested. Otherwise I end up with an extra line in between. (See 1 in the MWE).
  2. I can not have a blank line following the start of this environment when nested. This can be fixed by manually adding a \par\noindent but would prefer not to have to do that. (See 2 in the MWE).

enter image description here

As you can see below, I use a counter to count the depth of the nesting, and only \begin,\end the mdframed environment when the counter is zero. If there is a better way to detect that an environment is nested that would be useful as well.

Code:

\documentclass{article}
\usepackage{xcolor}
\usepackage{mdframed}

\newcommand*{\Text}{%
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
    Sed accumsan nulla ac ipsum elementum interdum. 
    Praesent ultricies faucibus turpis, non scelerisque nisi bibendum et.
}%

\newmdenv[%
    backgroundcolor=yellow!10,%
    linewidth=2pt,%
    skipabove=0pt,%
    skipbelow=0pt,%
    ]{MyBoxedEnvironmentMdframed}%

\newcounter{MyBoxEnvironmentDepth}%
\setcounter{MyBoxEnvironmentDepth}{0}% just to be clear we start at 0

\newenvironment{MyBoxedEnvironment}{%
    \ignorespaces%
    \par\noindent%
    \ifnum\value{MyBoxEnvironmentDepth}>0%
        % Ignore begin environment since it is nested.
    \else%
        \begin{MyBoxedEnvironmentMdframed}%
    \fi%
    \addtocounter{MyBoxEnvironmentDepth}{1}% increment depth
}{%
    \addtocounter{MyBoxEnvironmentDepth}{-1}% decrement depth
    \ifnum\value{MyBoxEnvironmentDepth}>0%
        % Ignore end environment since it is nested.
    \else%
        \end{MyBoxedEnvironmentMdframed}%
    \fi%
    \ignorespacesafterend%
}%

\begin{document}
\begin{MyBoxedEnvironment}

    \Text

    \par\noindent
    \textcolor{red}{\Text}

\end{MyBoxedEnvironment}

\begin{MyBoxedEnvironment}

    \Text

    \begin{MyBoxedEnvironment} % 1. Why space here matter?

        % 2. Why does blank line above yield extra line
        %\par\noindent% Don't want to have to add this
        \textcolor{red}{\Text}
    \end{MyBoxedEnvironment}
\end{MyBoxedEnvironment}
\end{document}

3 Answers 3

6

It seems you don't really need the counter just a switch If you define \foo to do \begin{MyBoxedEnvironmentMdframed} then your environment can call \foo\let\foo\relax and same for the end environment then in nested cases \foo will do nothing without needing a counter or explicit test.

updated example as requested

\documentclass{article}
\usepackage{xcolor}
\usepackage{mdframed}

\newcommand*{\Text}{%
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
    Sed accumsan nulla ac ipsum elementum interdum. 
    Praesent ultricies faucibus turpis, non scelerisque nisi bibendum et.%
}

\newmdenv[
    backgroundcolor=yellow!10,
    linewidth=2pt,
    skipabove=0pt,
    skipbelow=0pt,
    ]{MyBoxedEnvironmentMdframed}


\newenvironment{MyBoxedEnvironment}{%
    \def\MyBoxedEnvironment{%
    \let\endMyBoxedEnvironment\empty
    \par\noindent\ignorespaces}
    \begin{MyBoxedEnvironmentMdframed}%
    \par\noindent\ignorespaces
}{%
    \end{MyBoxedEnvironmentMdframed}%
    \ignorespacesafterend
}




\begin{document}
\begin{MyBoxedEnvironment}
\Text
\par\noindent
\textcolor{red}{\Text}
\end{MyBoxedEnvironment}

\begin{MyBoxedEnvironment}
\Text
\begin{MyBoxedEnvironment}
\textcolor{red}{\Text}
\end{MyBoxedEnvironment}
\end{MyBoxedEnvironment}
\end{document}

Edit: additional comments (which are apparently too long for the comment system)

Using \relax here is probably safe enough but it puts an unexpandable token into the stream whereas a space/newline does not, so it is not always safe to do that. \ignorespaces only ignores space tokens following it so if you follow it with some non-expandable token as in your MWE it does nothing. Similarly after you have done a \par blank lines will have no effect at all. Unless you are sure you only ever want text it's not normally a good idea to start with \noindent as that actually starts the paragraph, so nested display material would be incorrectly set. Better to set \parindent to zero if you just want non-indented paragraphs.

5
  • I am not sure I full understand this. I see that there is no problem with what you propose related to the \begin{}, but not how it would work with the \end{}? If I redefine the end environment how will I know when it ends? Feb 8, 2012 at 0:08
  • I'll edit some code into my answer, would probably have modified it originally but my TL2010 install gives ! Undefined control sequence. l.11 \newmdenv on your MWE and it;s midnight:-) Feb 8, 2012 at 0:13
  • Had to update both etoolbox and mdframed from tl2010 version to run the example, but updated as requested (taking @egreg's version as base) Feb 8, 2012 at 0:50
  • Wow!! I see that it works, but ...how? Can you add something about why the outermost \end{MyBoxedEnvironment} still works. Get some sleep first though... Is it a grouping effect? The revised definition of \MyBoxedEnvironment is only within the outer environment so the redefinition is only applied to the inner environments? Feb 8, 2012 at 1:06
  • More or less, yes. night:-) Feb 8, 2012 at 1:11
8

Two errors: the \ignorespaces must go last, after \par\noindent; the indentation in the nested environment is of course caused by the blank line after \begin{MyBoxedEnvironment}.

Here's a corrected version with properly placed % characters (for example, after 0 in the \ifnum lines there should not be a %).

\documentclass{article}
\usepackage{xcolor}
\usepackage{mdframed}

\newcommand*{\Text}{%
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
    Sed accumsan nulla ac ipsum elementum interdum. 
    Praesent ultricies faucibus turpis, non scelerisque nisi bibendum et.%
}

\newmdenv[
    backgroundcolor=yellow!10,
    linewidth=2pt,
    skipabove=0pt,
    skipbelow=0pt,
    ]{MyBoxedEnvironmentMdframed}

\newcounter{MyBoxEnvironmentDepth}
\setcounter{MyBoxEnvironmentDepth}{0}% just to be clear we start at 0

\newenvironment{MyBoxedEnvironment}{%
    \ifnum\value{MyBoxEnvironmentDepth}>0
        % Ignore begin environment since it is nested.
    \else
        \begin{MyBoxedEnvironmentMdframed}%
    \fi
    \addtocounter{MyBoxEnvironmentDepth}{1}% increment depth
    \par\noindent\ignorespaces
}{%
    \addtocounter{MyBoxEnvironmentDepth}{-1}% decrement depth
    \ifnum\value{MyBoxEnvironmentDepth}>0
        % Ignore end environment since it is nested.
    \else
        \end{MyBoxedEnvironmentMdframed}%
    \fi
    \ignorespacesafterend
}

\begin{document}
\begin{MyBoxedEnvironment}
\Text
\par\noindent
\textcolor{red}{\Text}
\end{MyBoxedEnvironment}

\begin{MyBoxedEnvironment}
\Text
\begin{MyBoxedEnvironment}
\textcolor{red}{\Text}
\end{MyBoxedEnvironment}
\end{MyBoxedEnvironment}
\end{document}

enter image description here

2
  • I prefer % at end of lines in the preamble so I changed the ones that end with a number to \relax% then things should be fine? I would prefer that blank lines at the begin of the environment to not result in an indent so that was why I had tried to put the \ignorespaces? Feb 8, 2012 at 0:19
  • A blank line can't be intercepted by \ignorespaces, that comes into action after the tokenization, when the empty line has been already converted into \par. You'd need a slower routine that ignores both spaces and \par tokens.
    – egreg
    Feb 8, 2012 at 10:11
4

I am not sure whether \ignorespaces here will do anything useful; try putting it at the very end of the definition of \begin{...} (here, after the counter increment). Also, why not include \par\noindent in the definition of your encironment, right before \ignorespaces? (Please excuse if I'm talking rubbish; it's 00:50 am here, and I'm looking at TeX.SE to rest after a hard day;).)

Also, never put percent signs after numerical constants!!! They should be followed by a space or newline, and if you don't obey that rule, someday a nasty, stupid bug will eat you!

2
  • 1
    As far as the numerical constants, I normally end them with \relax%, but missed it this time. Feb 8, 2012 at 0:20
  • +1 for "never put percent signs after numerical constants" Feb 23, 2013 at 0:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .