I'd like to inject a command into an arbitrary environment that uses the environment body as an argument.
Desired behavior
\begin{AnyEnvironment}{MandatoryEnvArg}[OptionalEnvArg]
...
\end{AnyEnvironment}
Should expand to
\begin{AnyEnvironment}{MandatoryEnvArg}[OptionalEnvArg]
\MyMacro{...}
\end{AnyEnvironment}
Where ... are arbitrary contents (possibly including \pars and other environments and commands).
Use Case
My use case for this perhaps odd question is the following: I want to be able to "preprocess" the environment body with \MyMacro. In particular, \MyMacro would be a command which itself invokes a Lua command with \directlua that would receive the environment body as a string and perform some string manipulation on it before putting it back into TeX's input stream. As a basic example:
\NewDocumentCommand{\MyMacro}{m}
{
\directlua
{
local str = "\luaescapestring{\unexpanded{#1}}"
tex.sprint(str .. ' ~ ' .. str)
}
}
Even more specifically, this all started because I was curious whether I could implement support for "shorthand" syntaxes (such as the one used in typst, a LaTeX-like typesetting program) in math mode. Basic experiments of mine yielded surprisingly nice results, so I became curious how far I could bring this kind of functionality in LaTeX, hence the generality of my question.
Requirements
AnyEnvironmentmay be any feasible LaTeX environment. In particular, it may:- Be user-defined.
- Be defined by external packages.
- Be inside of math mode (e.g.,
aligned), or activate math mode itself (e.g.,align). - Have any kinds of arguments.
- May have asterisks (
*) or dashes (-) in its name. - Contain verbatin content and/or catcode changes — It seems that supporting this would be very difficult (as per comments to this question).
Approaches
Redefining environment
\let\oldAnyEnvironment\AnyEnvironment
\let\endoldAnyEnvironment\endAnyEnvironment
\RenewDocumentEnvironment{AnyEnvironment}{+b}
{\oldAnyEnvironment\MyMacro{#1}}{\endoldAnyEnvironment}
Issues:
- Doesn't work if
AnyEnvironmenthas arguments - Doesn't work if
AnyEnvironmenthas*(asterisk) or-(dash) in its name (though this might be easily fixable, I've seen starred environments be referred to by\<env>starand\end<env>star, and maybe there's something similar for dashes).
With hooks
\ExplSyntaxOn
\NewDocumentCommand { \MyMacro } { m } { Argument:~#1. }
\AtBeginEnvironment { AnyEnvironment } { \MyMacro \bgroup }
\AtEndEnvironment { AnyEnvironment } { \egroup }
\ExplSyntaxOff
Issues:
- Doesn't work;
\bgroupand\egrouparen't the right thing to use for this purpose (also see Arguments possibly delimited by \bgroup and \egroup). However, this approach feels more promising to me. I tried reading into thel3basicsandl3quarkmodules, since I was hopeful that I could find an answer there; for example,\use_none_delimit_by_q_stop:w ... \q_stopmade me think that maybe something similar could be used to collect the environment body. However, I didn't manage to find a solution yet.
Related questions:
Related packages:
environ– A new interface for environments in LaTeX- Defines
\BODYand\Collect@Bodycommands
- Defines
xparse– A generic document command parser- Defines
band+bargument types
- Defines
amsmath– AMS mathematical facilities for LaTeX- Defines
\collect@bodycommand
- Defines
Basic non-working example:
\documentclass{article}
\ExplSyntaxOn
\makeatletter
\NewDocumentEnvironment { AnyEnvironment } {} {} {}
\NewDocumentCommand { \MyMacro } { m } { Contents: ~ #1. }
\AtBeginEnvironment { AnyEnvironment } { \MyMacro \bgroup }
\AtEndEnvironment { AnyEnvironment } { \egroup }
\makeatother
\ExplSyntaxOff
\begin{document}
\begin{AnyEnvironment}
Example.
\end{AnyEnvironment}
\end{document}