7

I have to gather, in a single source file, source files from several authors, each of them with its own (many) \newcommand, \newtheorem, etc. I'd like to limit the scope of each of these customizations, in the spirit of Limit macro/newcommand scope to section. The egreg's nice solution with \onlysectioncommands{...,...,...} has in my case the drawback that these \newcommand, \newtheorem, etc. may each time be numerous.

Hence, if each customization set is put in an auxiliary file, say ⟨author⟩-customization.tex, I'm looking for a clever \cleancustomization macro that lets me able to do something like:

...
%
\input{author1-customization}
\input{author1-source}
\cleancustomization{author1-customization}
%
\input{author2-customization}
\input{author2-source}
\cleancustomization{author2-customization}
%
\input{author3-customization}
\input{author3-source}
\cleancustomization{author3-customization}
%
...

In other words, \cleancustomization should be given a fixed list of customization macros (\newcommand, \newtheorem, etc.), and then be able to parse the file given in argument and automatically \undefine all the commands, theorems, etc. defined inside this file.

Do you see a (LaTeX3) way for doing this?

  • 1
    You could patch \newcommand, \newtheorem etc... so that they add the macros names in a list similar to egreg's \sectioncommandslist, then \cleancustomization would process it. – TonioElGringo Jan 15 '15 at 8:55
6

You can use something like this:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newcommand*{\saved@ifdefinable}{}
\newcommand*{\saved@rc@ifdefinable}{}
\newcommand{\startcustomization}{
    \def\@renewedcommands{}
    \def\@newedcommands{}
    \let\saved@ifdefinable\@ifdefinable
    \let\saved@rc@ifdefinable\@rc@ifdefinable
    \newcommand{\@saveforrestore}[1]{%
        \ifinlist{##1}{\@renewedcommands}{}{%
            \listadd\@renewedcommands##1%
            \expandafter\let\csname saved@\string##1\endcsname##1%
    }}
    \renewcommand{\@ifdefinable}[2]{%
        \listadd\@newedcommands##1%
        \saved@ifdefinable{##1}{##2}%
    }
    \let\@@ifdefinable\@ifdefinable
    \renewcommand{\@rc@ifdefinable}[2]{%
        \@saveforrestore##1%
        \saved@rc@ifdefinable{##1}{##2}%
    }
}
\newcommand{\cleancustomization}{%
    \newcommand{\saved@restore}[1]{\expandafter\let\expandafter##1\csname saved@\string##1\endcsname}%
    \forlistloop{\saved@restore}{\@renewedcommands}%
    \forlistloop{\undef}{\@newedcommands}%

    \let\@ifdefinable\saved@ifdefinable%
    \let\@rc@ifdefinable\saved@@ifdefinable%
}
\makeatother

\begin{document}

\startcustomization
    \newcommand*{\blipo}{Jill}
    \renewcommand\emph[1]{\textbf{#1}}
    Hello \blipo{}. \blipo{} is \emph{blonde}
\cleancustomization

Not defined here. \emph{Attempt} to use it--- ``generates'' error.
\end{document}

I defined two commands \startcustomization and \cleancustomization so that every command defined between the two will be undefined after \cleancustomization. I patched \@ifdefinable, as mentionned in this TeX.SE answer.

EDIT: Patching \@rc@ifdefinable, you can save the original macros when \renewcommand is used (in this code I save it in \saved@\macro, after checking that the macro isn't saved yet). This way, it is possible to restore the commands when \cleancustomization is used.

  • In order to insure this clever answer works in case of personal \renewcommands, it is sufficient (necessary?) to patch \@@ifdefinable exactly in the same way \@ifdefinable is. – Denis Bitouzé Jan 15 '15 at 18:04
  • 1
    @DenisBitouzé See my updated answer. The code is not so clean, but I hope it will help you. – TonioElGringo Jan 20 '15 at 10:11
2

May be too late but i find that we can do this with just grouping

\begingroup
\input{author1-customization}
\input{author1-source}
\endgroup
%
\begingroup
\input{author2-customization}
\input{author2-source}
\endgroup
%
\begingroup
\input{author3-customization}
\input{author3-source}
\endgroup
  • Surprisingly (at least for me), grouping is OK for \newcommand: \newcommand{\foo}{bar} in a first group doesn't conflict with \newcommand{\foo}{baz} in a subsequent group. But, unfortunately, this doesn't work for instance for \newtheorem: \newtheorem{theo}{Bar} in a first group conflicts with \newtheorem{theo}{Baz} in a subsequent group. And this is strange: though \foo was already defined and could be defined again, here the error message tells: ! LaTeX Error: Command \theo already defined. – Denis Bitouzé Feb 18 '15 at 13:57
  • 1
    @DenisBitouzé Ok. \newtheorem use internally \global\@namedef{#1} and \global\@namedef{end#1} soo grouping does not work – touhami Feb 18 '15 at 15:29

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.