Comment passer à la définition des commandes de #

est un projet ancien puisque c’est en 1999 que et détaillaient les motivations, les réalisations et le futur du Project[1].

Le projet a bien évolué et, depuis un certain temps déjà, l’idée de produire un nouveau format existant en parallèle de a été abandonnée : les membres du Project[2] ont décidé d’améliorer et de moderniser en intégrant graduellement dans son noyau les nouveautés, tout en veillant au maintien de la compatibilité descendante pour les anciens documents.

C’est ainsi que désormais, en 2022, nous avons accès aux mécanismes de sans rien charger de plus.

Cet article[3] se limitera à montrer quelques commandes, par le passé fournies par le package xparse mais désormais nativement présentes dans le format permettant de définir des commandes[4]. Il faudra se référer à la documentation usrquide pour davantage d’informations.

En réalité, nous nous limiterons à illustrer le passage de la classique commande \newcommand à \NewDocumentCommand, bien plus puissante. On sait en effet qu’une macro créée avec \newcommand a au plus 9 arguments obligatoires, dont au plus un (le premier) optionnel. Toutes les autres possibilités nécessitent, en pas mal de programmation ou des appels à d’autres packages. La commande \NewDocumentCommand permet de créer facilement des macros avec un mélange de différents types d’arguments.

Enfin, les macros créées avec \newcommand ne sont pas robustes. Sans rentrer dans les détails, disons que c’est la raison pour laquelle ces macros doivent dans certaines situations être précédées de la primitive \protect. En revanche, les macros créées avec \NewDocumentCommand sont par défaut robustes, ce qui augmente en général leur fiabilité.

1.  Description de la commande \NewDocumentCommand#

Pour créer des macros au niveau utilisateur[5], on dispose donc (entre autres) de :

  1. la commande \newcommand dont la syntaxe est la suivante :

    \newcommand{\⟨nom⟩}[⟨narg⟩]{⟨définition de la commande⟩}
    

    Nous ne rentrerons pas dans le détail des explications de cette commande très classique ;

  2. la commande \NewDocumentCommand qui offre un nouveau paradigme de programmation :

    \NewDocumentCommand{\⟨nom⟩}{⟨spéc. arg.⟩}{⟨définition dela commande⟩}
    

Dans les deux cas, les arguments successifs de la macro créée sont, dans la ⟨définition dela commande⟩, indiqués sous la forme #1, #2, etc.

Contrairement à \newcommand, \NewDocumentCommand ne doit pas seulement connaître le nombre d’arguments de la macro créée, mais aussi la nature de chacun. C’est dans son argument obligatoire ⟨spéc. arg.⟩ de spécification des arguments que ces natures seront spécifiées et, comme il est nécessaire de bien en comprendre le principe, nous allons y consacrer quelques lignes.

La forme générique de ⟨spéc. arg.⟩ est une liste de lettres où chacune déclare un type d’argument. Nous n’allons ici décrire que les plus communs et nous renvoyons vers la documentation usrquide pour avoir l’ensemble des types d’arguments.

m:

déclare un argument obligatoire standard qui peut-être une simple unité lexicale (token) ou un ensemble d’unités lexicales encapsulées entre accolades. C’est le type classique d’un argument normal.

o:

déclare un argument optionnel à fournir entre crochets dont la valeur sera -NoValue- s’il n’est pas fourni lors de l’utilisation. C’est l’équivalent de l’argument optionnel classique de

O{⟨défaut⟩}:

déclare un argument optionnel, comme la spécification o, mais affecte la valeur s’il n’est pas fourni lors de l’utilisation. C’est donc l’argument optionnel avec valeur par défaut.

s:

permet de déclarer une variante étoilée de la commande. L’argument aura pour valeur \BooleanTrue si la commande est appelée dans sa version étoilée, et \BooleanFalse sinon.

On constate donc que, contrairement au cas de \newcommand, on peut avoir autant d’arguments optionnels que l’on souhaite, et que tous peuvent avoir une valeur par défaut.

2.  Par l’exemple#

Illustrons par des exemples les quelques éléments introduits jusque-là.

2.1.  Commande sans argument#

Le cas le plus simple est celui d’une macro sans argument, typiquement une substitution de texte. À la sauce on écrit :

\newcommand\insertion{du texte à insérer}

Avec on fait comme expliqué précédemment, et comme la macro que l’on définit n’a pas d’argument, la liste de spécification des arguments est vide. Cela donne :

\NewDocumentCommand\insertion{}{du texte à insérer}

2.2.  Un ou plusieurs arguments obligatoires#

Regardons comment traiter en pratique les cas où l’on souhaite avoir un ou plusieurs arguments obligatoires.

En on peut créer les macros suivantes, respectivement à 1 et 2 arguments :

\newcommand\unargument[1]{Mon argument est #1}
\newcommand\deuxarguments[2]{Mes deux arguments sont #1 et #2}

Pour créer des macros équivalentes avec on va utiliser l’argument de spécification des arguments pour indiquer combien d’arguments obligatoires on souhaite.

\NewDocumentCommand\UnArgument{m}{Mon argument est #1}
\NewDocumentCommand\DeuxArguments{m m}{Mes deux arguments sont #1 et #2}

Il est d’usage de séparer les spécifications d’arguments par des espaces, mais ce n’est pas obligatoire.

Pour l’instant, les exemples ci-dessous montrent que tout ceci est très similaire au paradigme et c’est tant mieux. Nous allons voir que la puissance de cette nouvelle commande va se révéler lorsque les choses se compliquent.

2.3.  Un ou plusieurs arguments optionnels#

Avec la syntaxe de on peut avoir un seul argument optionnel qui se trouve entre crochets en première position. Pour cela, il faut utiliser le mécanisme de valeur par défaut du premier argument de la commande \newcommand.

\newcommand\unargopt[1][]{Mon argument est peut-être #1.}
\unargopt\par\unargopt[ceci]

Avec on va utiliser la spécification d’argument o. Ainsi, on écrira :

\NewDocumentCommand\UnArgOpt{o}{Mon argument est peut-être #1.}

Jusqu’ici, rien d’extraordinaire, mais fournit des mécanismes de test pour savoir si l’argument optionnel a été donné. Alors qu’avec on peut avoir besoin de packages supplémentaires (comme ifthen et ifmtarg), nous disposons ici de la commande \IfNoValueTF qui nous permet de faire des choses différentes suivant que l’argument optionnel a été renseigné ou non. Par exemple :

\NewDocumentCommand\UnArgOpt{o}{%
  Commande appelée
  \IfNoValueTF{#1}{%true
    \emph{sans} argument.%
  }%
  {%false
    \emph{avec} l'argument \og{}#1\fg{}.%
  }
}
\UnArgOpt\par\UnArgOpt[essai]

Avec ce nouveau mécanisme, il devient même très simple de créer une macro à deux arguments optionnels, par exemple se plaçant avant et après un argument obligatoire. Pour ce faire, on spécifiera les arguments à l’aide de la suite o m o.

\NewDocumentCommand\ExempleOMO{o m o}{%
         L'argument \emph{obligatoire}    est \emph{#2}.%
  \IfNoValueF{#1}{%
    \par L'argument \emph{optionnel} \no1 est \emph{#1}.%
  }%
  \IfNoValueF{#3}{%
    \par L'argument \emph{optionnel} \no2 est \emph{#3}.%
  }
}
\ExempleOMO[chat]{chien}[lapin]

2.4.  Arguments optionnels avec valeur par défaut#

Avec \newcommand, il est certes possible de déclarer un argument optionnel pourvu d’une valeur par défaut (qui peut être vide) mais on est limité à un unique tel argument. Avec on dispose de la spécification O{⟨défaut⟩} pouvant être employée plusieurs fois, comme l’illustre l’exemple suivant.

\NewDocumentCommand\Animal{O{le chien} O{beau}}{%
  Mon animal préféré est #1 car il est #2.%
}
\Animal\par\Animal[la baleine][gros]

2.5.  Commandes étoilées#

Avec en général, une macro peut avoir une version spéciale, portant le même \⟨nom⟩, mais étoilée : \⟨nom⟩*. Créer ces versions spéciales avec \newcommand est quelque peu délicat alors qu’avec \NewDocumentCommand et la spécification d’argument s, cela devient très simple. Nous avons vu que la spécification s renvoie un booléen et, à l’instar de \IfNoValueTF vu précédemment, on dispose de \IfBooleanTF.

\NewDocumentCommand\DeuxVersions{s m}{%
  Version
  \IfBooleanTF{#1}{%true
    étoilée
  }{%false
    classique
  }%
   qui utilise #2.%
}
\DeuxVersions*{étoile}\par\DeuxVersions{soleil}

3.  Pour aller plus loin#

Dans ce petit article, nous n’avons fait qu’effleurer les possibilités offertes par et, pour les nombreuses spécifications d’arguments disponibles, nous vous invitons à aller parcourir la documentation offerte par le usrguide.

Il s’agissait de présenter la commande\NewDocumentCommand par quelques exemples de base et de donner à imaginer ses potentialités, notamment concernant les simplifications qu’elle permet, et donc la complexification à moindre frais des commandes ainsi créées.

Notons qu’il existe la commande équivalente \NewDocumentEnvironment pour créer de nouveaux environnements, des commandes permettant de redéfinir des macros existantes, de nombreux autres tests (\If...), et bien plus encore.

Pour finir cette mise en bouche, un petit exemple illustrant un spécificateur d’argument optionnel permettant de spécifier ce par quoi cet argument doit être encadré.

\NewDocumentCommand\OptBarre{d<> o m}{%
  Nos arguments sont #3%
  \IfValueT{#1}{, #1}%
  \IfValueT{#2}{, #2}%
  .%
}
\OptBarre{chien}\par
\OptBarre<chat>{chien}\par
\OptBarre<chat>[lapin]{chien}