2

For my own needs, I am writing a package holding some environments definitions. I also have some commands that I want to be available only inside these environments. So I define these commands inside the environment definition.

My .sty files looks like this

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}[version 0.1]
\newenvironment{myenvirA}[1]
{
   \newcommand{\mycommandA}[1]
    { %
    %... do something
    }%
   % here, the "before" part of code 
}
{
   % here, the "after" part of code 
}


\newenvironment{myenvirB}[1]
{
   \newcommand{\mycommandB}[1]
    { %
    %.. do something
    }%
}
{
}

Now, say I want to make \mycommandA available inside myenvirB blocs. Sure, I can copy/paste, but that is bad programming (error prone).

Question: Does Latex provide a way to handle this case ?

If not, can I put that command definition in a file and \input it? (so I only have to define it once). Will that work once the package is installed in my Latex tree ?

Sorry if unclear, please point me on other question if this appears as a dupe. Couldn't find any, although this one is possibly related (?). But it is unclear to me if the answers apply to my question.

Edit forgot some information: the command has to have access to the environment arguments. Thus the idea given by Phelype Oleinik can't work (unless some trick I am not aware of ?).

Here is an MVCE:

main.tex:

\documentclass{article}
\usepackage{mypackage}
\begin{document}
\begin{envirA}{4}
\mycommandA{3}
\end{envirA}    
\begin{envirB}{4}
\mycommandB{3}
\end{envirB}  
\end{document}

mypackage.sty:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}[version 0.1]
\RequirePackage{tikz}

\newenvironment{envirA}[1]
{
    \newcommand{\mycommandA}[1]
    { 
        \draw (1,0) -- (##1,#1);
    }       
    \begin{tikzpicture}
    \draw (0,0) -- (#1,1);
}
{
    \end{tikzpicture}
}

\newenvironment{envirB}[1]
{
    \newcommand{\mycommandB}[1]
    { 
        \draw (0,1) -- (##1,#1);
    }       
    \begin{tikzpicture}
    \draw (0,0) -- (#1,1);
}
{
    \end{tikzpicture}
}
\endinput 
2
  • 2
    You can define the command outside the \newenvironment with a private name, say, \my@internal@command, and in the environment you can do \let\mycommand\my@internal@command. Commented Nov 2, 2018 at 18:39
  • Thanks, but while this appears to be the easiest way, it doesn't apply here because the command uses the environment arguments (with the ##1 syntax). And then this fails (I'll edit question to clarify this).
    – kebs
    Commented Nov 3, 2018 at 8:10

2 Answers 2

3

I guess that the definition of \mycommandA in myenvirA (I won't use numbers, which are illegal in command names) depends on the argument passed to the environment, otherwise the problem is easy to solve, by just defining \mycommandA directly.

\newcommand{\temporarycommandname}{} % initialize
\newenvironment{myenvirA}[1]
 {%
  \renewcommand{\temporarycommandname}[1]{something with #1 and ##1}%
  \global\let\mycommandA\temporarycommandname
  % whatever else
 }
 {%
  % the end part
 }

Now \mycommandA is usable everywhere, after \begin{myenvirA} has been executed. Further calls of myenvirA will change the meaning of \mycommandA, of course.

If you want that \mycommandA is defined only once, at the first call of myenvirA, and not modified by subsequent calls of the environment, do

\newcommand{\temporarycommandname}{} % initialize
\newenvironment{myenvirA}[1]
 {%
  \ifdefined\mycommandA\else
    \renewcommand{\temporarycommandname}[1]{something with #1 and ##1}%
    \global\let\mycommandA\temporarycommandname
  \fi
  % whatever else
 }
 {%
  % the end part
 }
4
  • Thanks for answer. Oops, yeah, sorry for number, I'll edit question to make it (at least) correct.
    – kebs
    Commented Nov 2, 2018 at 22:49
  • "I guess that the definition [...] depends on the argument passed to the environment" Yes, exactly, that is the point. I clarified the question. But, unless I missed something, your answer does not address the main point of the question, which was "how to define once a command that can be used in several environments". Or does it ? Because here I see the definition of command inside the environment. I don't get it...
    – kebs
    Commented Nov 3, 2018 at 8:27
  • @kebs If you want to do the definition only once, independently on how many times you call myenvirA, check for \mycommandA being defined or not. I'll add the variant.
    – egreg
    Commented Nov 3, 2018 at 10:15
  • Thanks for that edit, that's it, works perfectly! All I have to do is add all the allowed commands inside the first environment definition, inside the \ifdefined. Then, they get "globalized", thus available inside or outside of environments (will fail if used outside, of course), but keeping the benefit of the environments arguments. Great trick!
    – kebs
    Commented Nov 3, 2018 at 12:12
2

You can't grab a macro definition from another environment where it is defined, since the environment necessarily provides a limited scope for that macro (as it is grouped).

% \mycommand does not exist here
\begin{myenvironment}
  % _Local_ definition of \mycommand
  \newcommand{\mycommand}{<cmd definition>}
  % \mycommand exists here
  % ...
\end{myenvironment}
% \mycommand does not exist here

Perhaps there are ways to do that, but it's not worth the effort. You should instead define the macro to be globally available, or read it in from an external file via \input every time you need it (if properly installed within your installation folder). The latter option would slow down compilation.

The question you have to ask yourself is why you want to have the definition only be available locally to an environment. Do you want to save memory? That would have been a problem a couple of decades ago, but not anymore. Do you think it will provide for a cleaner package? If so, rather define a "namespace" by preceding each macro for your package mypackage by \@mypackage@. This way your macros will look like

\@mypackage@mycommandA
\@mypackage@mycommandB
...

The @ symbol in macros can be used without official declaration of a \makeatletter...\makeatother pair within a package, making it somewhat hidden to the end user. The idea of a "namespace" or @-macros is fairly common in packages.

6
  • Thanks for answer. Good point, but having to use \@mypackage@mycommandA is a constraint I'd rather avoid.
    – kebs
    Commented Nov 2, 2018 at 22:54
  • "why you want to have the definition only be available locally to an environment": this is because the command only makes sense inside the environment. This environment actually does some Tikz drawing, and the command inside adds some content inside the drawing.
    – kebs
    Commented Nov 2, 2018 at 22:56
  • "read it in from an external file via \input": Ok, just tried that. But there seems to be some pitfall. So: 1-put the command definition in file fcommandA (no extension) 2-inside the environment, do \input{fcommandA} => fails (Illegal parameter number in definition of \tikz@temp). Shall I give an extension to my file? Which one ?
    – kebs
    Commented Nov 3, 2018 at 8:43
  • @kebs: Use fcommand.tex. If you're still having issues, post a minimal example of what you're working with.
    – Werner
    Commented Nov 3, 2018 at 15:44
  • Mmmh. Yep, still having an issue with \input. I'll post a separate question about this, there is something disturbing about this. I'll let you know here.
    – kebs
    Commented Nov 3, 2018 at 16:57

You must log in to answer this question.

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