LaTeX commands and environments
Throughout this series, we have used various commands and environments from multiple LaTeX packages. It's time to do a brief overview of various ways to create your own.
Contents
Built-in capabilities β©
def β©
\def<\command><params>{<body>}
This is the simplest way to define a command in LaTeX.
\def\mydef{%
This is expanded from \textbackslash mydef.%
}
Then, when invoked in the document as \mydef, it will expand to This is expanded from \mydef. The comment marks (%) are added to be sure that no extra new lines are added to the output.
You can also specify the command arguments, from #1 to #9:
\def\mydef#1#2{%
Argument 1: #1;\\
Argument 2: #2.%
}
When called like \mydef{First argument}{Second argument}, this will output the following text:
Argument 1: First argument;
Argument 2: Second argument.
Since \def won't check if the command is already defined, you might want to check for this manually:
\ifx <\command> \undefined
\def <\command>
\fi
Generally, you would want to use a newcommand macro instead of \def since it checks for this automatically and provides additional useful functionality.
let β©
\let<\new><\old> copies the contents of an existing command into a new one. Thus, you can save the behaviour of a command before modifying it. For example, you could save the first \mydef into \olddef before redefining it with:
\let\olddef\mydef
newcommand β©
\newcommand{<\command>}[<n>][<default>]{<body>}
The definition is similar to \def, but you specify the number of arguments in square brackets following the command name. The first argument can also take the default value that should follow the number of arguments, also surrounded by square brackets:
\newcommand{\mycmd}[2][default]{%
Argument 1: #1;\\
Argument 2: #2.%
}
The default argument should also be passed in square brackets, so when called like \mycmd[Arg 1]{Arg 2}, the command will output:
Argument 1: Arg 1;
Argument 2: Arg 2.
And when called like \mycmd{Arg 2} it will output:
Argument 1: default;
Argument 2: Arg 2.
You can also redefine an existing command with \renewcommand with the same syntax. This ensures that you won't accidentally overwrite other commands.
Starred commands β©
As you could have already seen, some LaTeX commands come in "starred" versions with an asterisk after the command's name, for example, inline lists of the enumitem package. You can also do such commands yourself by employing the \@ifstar command:
\makeatletter
\newcommand*{\mystar@starred}[1]{Starred \textbackslash mystar command with argument #1.}
\newcommand*{\mystar@normal}[1]{Normal \textbackslash mystar command with argument #1.}
\newcommand*{\mystar}{\@ifstar{\mystar@starred}{\mystar@normal}}
\makeatother
\mystar{Arg 1}
\mystar*{Arg 1}
Since \@ifstar contains an @ symbol, the whole thing should be enclosed between the \makeatletter and \makeatother commands.
π NOTE: \newcommand and \renewcommand commands also have starred versions, which forbid the arguments from containing multiple paragraphs of text.
newenvironment β©
In the same way, you can define your own custom environments:
\newenvironment{<environment>}[<n>][<default>]{<before>}{<after>}
(And redefine them with the \renewenvironment command.)
For example, the following environment will produce a framed tableβ¦
\newenvironment{mytab}[1]{%
\begin{tabular}{|#1|}%
\hline
}{%
\hline
\end{tabular}%
}
β¦ when called like:
\begin{mytab}{cl}
a & b \\
c & d \\
\end{mytab}
π NOTE: You don't need to use \@ifstar for the starred environments, as you can safely declare an environment with an asterisk in its name.
xparse β©
The xparse package1 provides a collection of advanced macros for defining commands and environments, such as \NewDocumentCommand and \NewDocumentEnvironment.
The powerful syntax allows for the definition of various mandatory and optional arguments.
Mandatory arguments:
m- standard mandatory argument enclosed between curly braces;r<a><b>- required delimited argument enclosed between the specifiedaandbtokens;R<a><b><error>- same, but with a customerrorstring instead of-NoValue-;v- verbatim argument, must be enclosed between two identical characters (except%,\,#, or).
Here's an example of a command with different types of mandatory arguments. Try to omit some of them and see how LaTeX reacts:
\NewDocumentCommand{\mynewcmdM}{ r<> R`'{n/a} m v}{%
Argument 1 (required delimited): #1\\
Argument 2 (req. delim. user-defined default): #2\\
Argument 3 (mandatory): #3\\
Argument 4 (verbatim): #4%
}
\mynewcmdM<r\textless\textgreater>`R`''{m}+v \v3rb@t!m\+
Optional arguments:
o- standard optional argument enclosed between square brackets;O{default}- same, but with a customdefaultvalue;d<a><b>- optional delimited argument enclosed between the specifiedaandbtokens;D<a><b>{default}- same, but with a customdefaultvalue;s- an optional star, equal to\BooleanTrueif present or to\BooleanFalseotherwise;t<x>same, but with a customxtoken;e{tokens}- optional embellishments, each requiring a value (otherwise,-NoValue-is returned);E{tokens}{defaults}- same, with a list of defaults for each embellishment.
Here's an example of a command with different types of optional arguments. Naturally, you can call it without any arguments specified, or with all of them:
\NewDocumentCommand{\mynewcmdO}{ s o O{O\{\}} d`' D++{D++} t! e{_^} E{@}{{default bold text}} }{%
Argument 1 (star): \IfBooleanTF{#1}{star}{no star}\\
Argument 2 (optional): #2\\
Argument 3 (optional, user-defined default): #3\\
Argument 4 (optional delimited): #4\\
Argument 5 (opt. delim, user-defined default): #5\\
Argument 6 (custom token `!'): \IfBooleanTF{#6}{exclamation}{no exclamation}\\
Argument 7 (embellishment \_): \IfValueTF{#7}{$_#7$}{no subscript}\\
Argument 8 (embellishment \^): \IfValueTF{#8}{$^#8$}{no superscript}\\
Argument 9 (embellishment \@, user-defined default): \textbf{#9}%
}
\mynewcmdO
\mynewcmdO*[o][O]`d'+D+!_a^b@c
As you've already seen from the examples, the package also provides a set of commands to check the values of the arguments. Each of them comes with the same set of logic branch suffixes (T - if true, F - if false, or TF - both):
\IfBollean<TF>;\IfValue<TF>;\IfNoValue<TF>.
The environments (\NewDocumentEnvironment) work pretty much the same, but have an additional mandatory b argument to manipulate the environment's body:
\NewDocumentEnvironment{twice}{ O{\ttfamily} +!b }
{#2#1#2}
{}
\begin{twice}[\itshape]
Hello world!
\end{twice}
The + prefix allows multiple paragraphs in the body, and the ! prefix disables the space-trimming that is done by default.
Discuss this post on Reddit