# How do I redefine a macro when any group begins, then again when that group ends?

Okay, I know this is a pretty tall order, but I'm working on a package based on my answer here, and I'm including Ahmed Musa's environment which redefines \par. The problems are:

1. Some environments (e.g., lists) use \par internally in ways that I don't want to touch.
2. Other environments (e.g., also lists) clobber my redefinition of \par globally.
3. There should be an easy way to exclude a set of paragraphs from the \par redefinition.

And it's all well and good to just explain in the documentation that the environment which redefines \par is for convenience only and that you should only really put flat text in it. But I was wondering if there was a better way.

What I want to do, if possible, is redefine the macro (really \par, but let's call it \a=\a@new) in such a way that when any environment or {-delimited group begins, it gets set to \a@old. And then when that specific environment or group ends, it should be set right back to \a@new, even if \a got globally redefined in the interim.

So, disregarding whitespace,

\def\a{old a}
\def\a@new{new a}

\a
\begin{redefa}
\a
{\a}
{
\gdef\a{\emph{weird} a}
\a
{\a}
}
\a
\end{redefa}
\a


Should give

old a
new a
old a
weird a
weird a
new a
old a

Is this even remotely possible?

-
do you really need to \gdef this? if you simply \def (or better, for latex, \renewcommand{\a}) inside the group, then it will automatically go back at the end of the group to its definition before the group was opened. –  barbara beeton Dec 14 '11 at 19:08
There are a lot of groups inside package code. What you are asking to do is the direct opposite of the way groups usually work: I suspect that confusion lies on this path. –  Joseph Wright Dec 14 '11 at 19:10
@barbarabeeton \gdef is what other packages apparently do, and I'm trying to clean up after that. @JosephWright Yeah, I know this could mean sanitizing a hack with an even worse hack. But at least partly I'm asking this out of curiosity. –  rdhs Dec 14 '11 at 19:11
Getting \a{\a} to use two different defs for \a can only be done using \currentgrouplevel or \currentgrouptype. Reverting the \gdef\a{weird} at the end of the {} group is doable with \aftergroup. –  Bruno Le Floch Dec 14 '11 at 20:12
@BrunoLeFloch Holy crap, \currentgrouplevel is exactly what I wanted before I asked the question. If I combine that with some everyhook stuff, I think that'd put me well on my way to a solution. Thanks! –  rdhs Dec 14 '11 at 20:45

NOTE: This is what I hacked together for my specific problem. If anyone comes up with a cleaner or more general answer, I'll accept that instead.

Thanks to Bruno Le Floch for suggesting \currentgrouplevel!

everyhook provides a very secure way to append stuff to \everypar, which lets me run this at the start of every paragraph.

First, I check for environment changes using \@currenvir. Then, to defeat {-delimited groups, I also check that the paragraph is at the same \currentgrouplevel as \begin{autopn}. If both checks pass, \par gets redefined once only – the next time it is used, it assumes the value it had before \begin{autopn}.

But if the checks keep passing, \par keeps getting redefined. So if something \gdef's \par, \par will just get redefined anyways once the group passes back to the autopn environment.

\RequirePackage[excludeor]{everyhook}

\newcount\PN@env@depth
\def\@PN@autopn{autopn}
\newenvironment{autopn}
{\PN@env@depth=\currentgrouplevel}
{\parnotes}
\PushPostHook{par}{%
% check for environment changes
\ifx\@currenvir\@PN@autopn
% check for {-delimited groups
\ifnum\currentgrouplevel=\PN@env@depth
\let\PN@par@orig\par
\def\par{%
\PN@par@orig
%
% check again at the end of the paragraph in case the paragraph spans groups
%
\ifx\@currenvir\@PN@autopn
\ifnum\currentgrouplevel=\PN@env@depth
% the new code I wanted to inject
\parnotes
\fi
\fi
% restore the definition of \par; then everything just repeats next paragraph
\let\par\PN@par@orig
}
\fi
\fi}

-

The etextool package adds begin- and end-hooks to all environments. You can use these to exclude certain environment, i.e. remove or change your definition accordantly. See the package manual for the details.

-
I know it sounds stupid when I come right out and say it, but I'm looking to do this for all environments, and {-delimited groups. Also, I've tried using etoolbox's \AtBeginEnvironment{list}{...} to just get this behavior for lists, and it did nothing – I'll put a MWE in another question if this one is unresolvable. –  rdhs Dec 14 '11 at 19:10