11

Yesterday I was reading the etoolbox documentation where there are defined some new document commands such as:

\Before<Begin/End>Environment{<envir>}{<code>}
%
\At<Begin/End>Environment{<envir>}{<code>}
%
\After<Begin/End>Environment{<envir>}{<code>}

Which I found very useful in dealing fine-tuning macro expansion inside or outside specific environments.

The main question however hit me when I wanted to define a new command, based on the above, that looks like this:

\If<Before/At/After><Begin/End>Environment{<envir>}{<code>}{%
 <yes>%
}{%
 <no>%
}

The main action of this code is that it checks if a specific control sequence name or macro/code <code> appears <Before/At/After><Begin/End> the environment and then applies the code if it is met the condition stated, and does else otherwise. This can be similar to the standard if-clause conditional very well known inside plain TeX (maybe some TeXpert could put an eye to this macro).

The main reason on why it reads the control sequence name it's that if there is some macro behind a specific environment, it doesn't expand its arguments and keeps reading it as a csname.

So my question can be expressed as: how this macro can be created? is there some command that simulates its action that I'm not aware of?

I basically couldn't find any documentation or source even remotely related to this application on macro setting.

  • Your listed commands are basically hacks of the commands \begin and \end. So you can put a simple if-clause inside the second mandatory argument of \At<Begin/End>Environment{<envir>}{<code>}. – Marco Daniel Sep 19 '13 at 11:59
  • The main problem could be like: I want to know if \mymacro is right before listings, but I do not know how this could be implemented. I'm saying this because if I'd do \ifdefined\mymacro\else... would be wrong because \mymacro is always defined. – alandella Sep 19 '13 at 12:01
  • You haven't defined what you mean by before do you mean literally anywhere before a listings environment in the source, do you mean immediately before (and how do you define immediately does white space or a blank line (\par) between them stop it being immedatiate) or do you mean "is this command being run from the before-hook of an enviornment added by the etoolbox package?" – David Carlisle Sep 19 '13 at 12:21
  • @DavidCarlisle I'm (trying) to say that the <code> inside If<Before/At/After><Begin/End>Environment has to be verified if it is immediately before/after my environment, without any \par that separates it. From your third option (I'm so sorry) I didn't understand well though. – alandella Sep 19 '13 at 12:24
  • What I mean is, because you introduced your question with \Before<Begin/End>Environment{<envir>}{<code>} I would have assumed that you were asking how <code> could check that it was being run by that command in (for example) \Before{listings} but I don't think you mean that (so etoolbox probably isn't relevant to your question?) – David Carlisle Sep 19 '13 at 12:29
9
\documentclass{article}
\usepackage{listings}
\def\foo{\futurelet\tmp\xfoo}
\def\xfoo{\ifx\tmp\begin\expandafter\xxfoo\else\expandafter\nolistingsfoo\fi}
\def\xxfoo#1#2{\def\tmp{#2}%
  \ifx\tmp\xlistings\expandafter\listingsfoo\else\expandafter\nolistingsfoo\fi
  #1{#2}}
\def\xlistings{lstlisting}

\def\listingsfoo{\typeout{lstlisting is next}}
\def\nolistingsfoo{\typeout{lstlisting is not next}}

\begin{document}

\foo
\begin{lstlisting}
\end{lstlisting}

\foo
\begin{center}
\end{center}

\foo xxxx
\begin{lstlisting}
\end{lstlisting}

\end{document}

produces

lstlisting is next
lstlisting is not next
lstlisting is not next
  • Too bad that double upvote doesn't exist :-), so now I could substitute accurately to obtain the needed code before the environment. OK so far, but what about (only for coherence) the after environment (just to inclue both cases)? – alandella Sep 19 '13 at 12:41
  • well basically you can't check backwards. You could redefine \end to look forwards as above for your code, bu that isn't quite the same – David Carlisle Sep 19 '13 at 12:49
2

Longer code than David's, but shows how to do it in a more abstract way.

\documentclass{article}
\usepackage{xparse,listings}

\ExplSyntaxOn
\NewDocumentCommand{\newcheckcommand}{mm}
 {
  \andrea_new_check_command:Nn #1 { #2 }
 }

\tl_new:N \l_andrea_prefix_tl
\cs_new_protected:Npn \andrea_new_check_command:Nn #1 #2
 {
  \cs_if_exist:NTF #1
   { Damn! } % probably a better message is needed
   { \andrea_def_check_command:Nn #1 { #2 } }
 }
\cs_new_protected:Npn \andrea_def_check_command:Nn #1 #2
 {
  \cs_new:Npn #1 { \andrea_check:N #1 }
  \tl_set:Nx \l_andrea_prefix_tl { \cs_to_str:N #1 @ }
  \keys_set:nn { andrea/check } { default = {} }
  \keys_set:nn { andrea/check } { #2 }
 }
\keys_define:nn { andrea/check }
 {
  unknown .code:n = \cs_new:cpn { \l_andrea_prefix_tl \l_keys_key_tl } { #1 } ,
  default .code:n = \cs_gset:cpn { \l_andrea_prefix_tl default } { #1 } ,
 }
\cs_new:Npn \andrea_check:N #1
 {
  \peek_meaning_ignore_spaces:NTF \begin
   {
    \andrea_exec:NNn #1
   }
   {
    \use:c { \cs_to_str:N #1 @ default }
   }
 }
\cs_new:Npn \andrea_exec:NNn #1 #2 #3
 {
  % #2 is \begin, #3 is the environment's name
  \cs_if_exist:cTF { \cs_to_str:N #1 @ #3 }
   {
    \use:c { \cs_to_str:N #1 @ #3 }
   }
   {
    \use:c { \cs_to_str:N #1 @ default }
   }
  #2{#3} % put back the \begin{...}
 }
\ExplSyntaxOff

\newcheckcommand{\foo}{
  lstlisting = \texttt{lstlisting} comes next,
  quotation  = \texttt{quotation} comes next,
  default    = I don't know what to do
}

\begin{document}
\foo
\begin{lstlisting}
x
\end{lstlisting}

\foo
\begin{center}
y
\end{center}

\foo
\begin{quotation}
z
\end{quotation}

\foo xxxx
\begin{lstlisting}
t
\end{lstlisting}

\end{document}

enter image description here

With the call \newcheckcommand{\foo}{...} a set of macros is defined; the only known key is default; if it's not set in the argument, there will be nothing executed, because the default macro is initialized to empty.

  • The implementation of xparse/expl3 codind looks very nice by the way, certainly the +1 is deserved, but now I really do not know what to accept because both answers are detailed, exaustive and appropriate (why there isn't a two-acceptable answer?):-( – alandella Sep 19 '13 at 16:12
  • @AndreaL. The basic idea is the same as David's. – egreg Sep 19 '13 at 16:17
  • Now that two main options have been shown to me, I think it's necessary to accept the first answer (even if I think that both can deserve +25), without saying that yours isn't important to my purposes (thank you for your time). – alandella Sep 19 '13 at 16:21

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.