xparse (\NewDocumentCommand)

Dès que vous voulez une commande avec plusieurs arguments optionnels ou une vraie variante étoilée, compter les arguments à la manière de \newcommand ne suffit plus. La réponse moderne est \NewDocumentCommand, qui déclare le *type* de chaque argument par une chaîne de lettres. Né dans le package xparse, il fait aujourd’hui partie du noyau LaTeX. Cette page couvre la mini-langue de spécification des arguments et les commandes de test qui inspectent ce que l’utilisateur a fourni.

Désormais dans le noyau

Autrefois, \usepackage{xparse} était nécessaire ; depuis la version LaTeX d’octobre 2020, ce mécanisme vit dans le noyau lui-même. Des commandes comme \NewDocumentCommand sont disponibles d’emblée, et charger xparse est rarement utile. Le fichier du noyau qui les implémente s’appelle ltcmd, et le package xparse est officiellement qualifié d’« obsolete ». À part quelques types d’arguments obsolètes, le nouveau code peut utiliser ces commandes sans rien charger.

L’idée de cette interface est de séparer le sens, donc l’interface, de l’implémentation. On déclare la suite d’arguments visible par l’utilisateur — obligatoires, optionnels, étoilés — comme une spécification d’arguments (arg-spec), puis le corps reçoit toujours des arguments normalisés sous la forme #1, #2, etc. Les bases de \newcommand sont sur la page « Définir des macros » ; les connaître rend le contraste plus clair.

Définir commandes et environnements

La forme de base est \NewDocumentCommand{\cmd}{⟨arg-spec⟩}{⟨code⟩} : nom de commande, spécification d’arguments, corps. Comme \newcommand, elle possède des variantes selon le but ; ce qui change est leur comportement face à un nom déjà défini.

CommandeFace à un nom existant
\NewDocumentCommandErreur si déjà défini
\RenewDocumentCommandErreur si pas encore défini (le retravaille)
\ProvideDocumentCommandDéfinit seulement si absent
\DeclareDocumentCommandÉcrase toujours (à utiliser avec retenue)

Les environnements ont le même traitement avec \NewDocumentEnvironment{⟨env⟩}{⟨arg-spec⟩}{⟨start code⟩}{⟨end code⟩} (et \Renew…, \Provide…, \Declare…). Les arguments sont donnés après \begin{⟨env⟩} et visibles dans le code de début comme dans celui de fin ; voir la page sur les environnements personnalisés. Bonus : toute commande créée ainsi est automatiquement robuste (via le mécanisme \protected d’e-TeX), donc pas besoin de \protect dans un titre ou une légende.

Il existe aussi une variante expansible, \NewExpandableDocumentCommand. Elle s’utilise dans des contextes où la commande est développée, par exemple dans \edef ou \write, mais les types d’arguments autorisés sont restreints : les tests booléens comme s/t et le verbatim v ne sont pas disponibles. Pour un document ordinaire, la forme standard est celle à choisir.

Langage de spécification des arguments

Une spécification d’arguments est une chaîne où chaque lettre nomme le type d’un argument : obligatoire, optionnel, étoilé, délimité par certains caractères, etc. Chaque type arrive dans le corps comme #1, #2, etc., mais le point subtil est qu’un argument optionnel non fourni reçoit une valeur spéciale. Les principaux types sont résumés ci-dessous.

TypeÉcritureSensArrivée dans le corps
mmArgument obligatoire {}Un #1 ordinaire
ooArgument optionnel [...]-NoValue- si absent
OO{default}Optionnel avec valeur par défautdefault si absent
ssÉtoile optionnelle *\BooleanTrue / \BooleanFalse
tt⟨char⟩Token caractère optionnel donné\BooleanTrue / \BooleanFalse
rr⟨d1⟩⟨d2⟩Obligatoire, délimité par d1d2-NoValue- après erreur si absent
dd⟨d1⟩⟨d2⟩Optionnel, délimité par d1d2-NoValue- si absent
ee{⟨tokens⟩}Embellissements, par ex. ^, _Chacun -NoValue- si absent
vvArgument verbatimLes caractères littéraux

m est l’argument obligatoire de base : il accepte un groupe entre accolades ou un token unique, et les {} extérieures sont retirées avant que le corps ne le voie. o est l’argument optionnel [...] standard, et O{default} le même avec valeur par défaut. Les types majuscules comme O et D permettent de donner une valeur par défaut, tandis que leurs versions minuscules o/d renvoient le marqueur spécial -NoValue-.

s détecte une étoile * en tête : présente, elle donne \BooleanTrue, absente, \BooleanFalse. La généralisation à un caractère unique est t⟨char⟩ ; t+, par exemple, signale la présence de + comme booléen. r⟨d1⟩⟨d2⟩ est un argument délimité obligatoire, entouré de délimiteurs choisis, par exemple r() pour des parenthèses ; si le délimiteur ouvrant manque, il émet une erreur et insère -NoValue-. La version optionnelle délimitée est d⟨d1⟩⟨d2⟩.

Plus particulier, e{⟨tokens⟩} rassemble des embellissements comme ^ et _. Avec e{^_}, il récupère ^{...} et _{...} dans n’importe quel ordre, et ceux qui manquent deviennent -NoValue- ; les tokens listés doivent être distincts. v lit un argument verbatim, comme \verb. Les délimiteurs des types délimités (r, d, …) ne peuvent pas être les caractères de groupement TeX { et } ; on choisit donc des paires naturelles comme [], () ou "".

Inspecter les arguments : les tests

Pour gérer un argument optionnel ou une étoile, il faut tester dans le corps s’il a été fourni. Deux familles de commandes le font. \IfNoValueTF{#1}{⟨if no value⟩}{⟨if value⟩} vérifie si l’argument vaut -NoValue-, c’est-à-dire s’il est absent : absent, il exécute la première branche ; présent, la seconde. L’inverse \IfValueTF existe aussi, ainsi que les formes à une seule branche (\IfNoValueT / \IfNoValueF, \IfValueT / \IfValueF).

Pour le booléen produit par s ou t, utilisez \IfBooleanTF{#1}{⟨if starred⟩}{⟨if not⟩} : si #1 vaut \BooleanTrue, la première branche s’exécute ; avec \BooleanFalse, la seconde. Les formes \IfBooleanT / \IfBooleanF existent aussi. Règle pratique : \IfNoValueTF et proches servent aux arguments optionnels comme o/d; \IfBooleanTF et proches aux booléens s/t. Enfin, -NoValue- est conçu pour ne pas correspondre au texte littéral -NoValue- ; testez-le toujours avec \IfNoValueTF plutôt qu’en comparant des chaînes.

Exemple : étoile plus option avec défaut

Construisons une commande avec variante étoilée et argument optionnel à valeur par défaut, grâce à la spécification s O{} m. L’exemple est une commande de titre : #1 est l’étoile, testée par \IfBooleanTF; #2 le titre court optionnel, vide par défaut ; #3 le titre principal.

latex
\NewDocumentCommand{\heading}{s O{} m}{%
  \IfBooleanTF{#1}%
    {\textbf{#3}}%                       星つき: 番号なしの見出し / starred: unnumbered
    {\section%
       \IfNoValueTF{#2}%
         {{#3}}%                          短縮見出しなし / no short title
         {[#2]{#3}}%                       短縮見出しあり / with short title
    }%
}

% 使用例 / usage
\heading{Introduction}             % 通常の番号つき節
\heading[Intro]{A Long Introduction} % 目次用に短縮見出しを指定
\heading*{Preface}                 % 星つき: 太字の無番号見出し

Subtilité : ne confondez pas o et O{}. Avec o, un argument absent devient -NoValue-, donc on branche avec \IfNoValueTF comme ci-dessus. Avec O{}, au contraire, une valeur est toujours présente, vide si omise ; on teste alors le contenu vide, avec \tl_if_blank:nTF (expl3) ou \ifblank (etoolbox), pas avec \IfNoValueTF. Dans l’exemple, il faudrait donc un argument optionnel o sans valeur par défaut pour que la branche \IfNoValueTF ait le comportement voulu.

Pourquoi c’est mieux que \newcommand

\newcommand n’autorise qu’un seul argument optionnel, le premier, et ne fournit pas de variante étoilée intégrée. Le contournement classique consistait à écrire à la main des mécanismes bas niveau comme \@ifstar et \@ifnextchar, mais ils exigent \makeatletter, sont fragiles face aux espaces et aux imbrications, et peu lisibles.

  • Plusieurs arguments optionnels indépendants (o o m, O{a} O{b} m, …).
  • Vraies variantes étoilées avec s plus \IfBooleanTF, sans bricolage \@ifstar.
  • Valeurs par défaut déclaratives : O{default} suffit à fixer la valeur quand elle est omise.
  • Robuste dès le départ : pas de \protect dans les titres ou légendes.
  • Des syntaxes d’entrée que \newcommand ne peut pas exprimer : arguments délimités (r, d) et embellissements (e).

En résumé, une simple abréviation convient très bien à \newcommand, mais dès que la structure des arguments devient un peu élaborée, \NewDocumentCommand est le premier choix. Quand le corps doit ensuite devenir un petit programme, l’associer à expl3, la couche de programmation LaTeX3, est l’étape naturelle. Pour créer classes et packages, voir la page d’auteur.