16

I encountered a peculiarity concerning \NewDocumentCommand arguments. In a case where I had real long names in the arguments, I wanted to put them each on its own line. The first ones are OK, the 2 last ones are problematic, as shown in the MWE. Is this the normal behaviour or is it a bug or am I doing something stupid?

\documentclass{article}
\usepackage{xparse}
%                           12  3        4          56     
\NewDocumentCommand\TestArg{st+ O{\empty}D(){\empty}mD()%
                              {\empty}O{\empty}}{%
%                                     7
\noindent
3: #3 \\
4: #4 \\
5: #5 \\
6: #6 \\
7: #7
}
\begin{document}
\TestArg[Three](Four){Five}(Six)[Seven]
\par\vspace{2\baselineskip}
\TestArg
[Three]
(Four)
{Five}
(Six)
[Seven]
\end{document}
  • A percent sign at the end of the lines would solve the problem... I guess that this happens because some equivalent of \@ifnextchar sees the space at the end of the line instead of the ( or [. I don't know why this doesn't happen for the first ones, though. – Phelype Oleinik Mar 21 '18 at 23:18
  • I know that a percent sign solves it, but I wonder whether this is normal. – Jack Mar 21 '18 at 23:27
  • 1
    I'm uncertain why you would separate D() on one line and {\empty} on the next. By the way, why {\empty} and not {}? – egreg Mar 21 '18 at 23:29
  • 1
    By a precise choice when xparse was being developed, optional arguments cannot be on a new line; precisely, there cannot even be a space before them. – egreg Mar 21 '18 at 23:32
  • 1
    @PhelypeOleinik see the amsmath package which changes the standard \@ifnextchar handling so that \\ newline [a+b] in the first array cell of the next line is not misinterpreted as \\[a+b] and give an error that a+b isn't a length – David Carlisle Mar 21 '18 at 23:43
17

Let's consider a simpler example:

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand\TestArg{mO{x}}{%
  1: #1, 2: #2
}

\begin{document}

\TestArg{a}[b]

\TestArg{a} [b]

\end{document}

This produces

enter image description here

You can clearly see that in the second case [b] is not recognized as an optional argument; instead, the default value x is used and [b] is printed separately, with a space in front of it.

A new line counts as a space, so it's not a surprise if also

\TestArg
{a}
[b]

produces the same result.

This was a precise choice when the interface of xparse was developed. The team followed the example of amsmath that doesn't allow spaces in front of optional arguments to \\, because of the infamous

\left[\begin{array}{cc}
1 & 2 \\
[1] & [2]
\end{array}\right]

that raises an error, because [1] is mistaken for the optional argument to \\. Instead

\begin{bmatrix}
1 & 2 \\
[1] & [2]
\end{bmatrix}

works flawlessly.

The input \TestArg{a} [b] is deemed ambiguous. There were two choices, the team followed the path of considering [b] not an optional argument.

You get the same behavior with

\documentclass{article}
\usepackage{xparse}

\NewDocumentCommand\TestArg{mD(){x}}{%
  1: #1, 2: #2
}

\begin{document}

\TestArg{a}(b)

\TestArg{a} (b)

\end{document}

In my opinion, one should never define macros with seven arguments. In your case two are for the variants, four are optional and one is mandatory.

A key-value interface would be much better:

\TestArg[
  var-a=true,
  var-b=false,
  opt-A=Three,
  opt-B=Four,
  opt-C=Six,
  opt-D=Seven,
]{Five}

By the way, {\empty} does not specify an empty default value: if tested with \tl_if_empty:nTF{#3}, a missing first optional argument would make LaTeX follow the false branch.

  • Perhaps worth adding that it's still not clear (at least to me) if we should have the current behaviour or not: the \\ case is perhaps 'special' and one might argue it should be handled separately from everything else. – Joseph Wright Mar 22 '18 at 7:13
  • @egreg: What signature would this key-val solution have? – Jack Mar 22 '18 at 8:32
  • @Jack That would be {O{}m}. Defining the key-value interface requires going into \ExplSyntaxOn, but for such complex commands this is really better. – egreg Mar 22 '18 at 8:34
  • @egreg: Sorry to bother you again. Can you also show me the body of the command. It is a bit complexer than I thought. Thanks! – Jack Mar 22 '18 at 8:42
  • @Jack It would be easier if you show your actual situation, with an example of use and the details about the optional arguments and the variants. Open a new question, please. – egreg Mar 22 '18 at 8:54
9

you can not have white space before optional arguments that follow the last mandatory argument so this works.

\documentclass{article}
\usepackage{xparse}
%                           12  3        4          56     
\NewDocumentCommand\TestArg{st+ O{\empty}D(){\empty}mD()%
                              {\empty}O{\empty}}{%
%                                     7
\noindent
3: #3 \\
4: #4 \\
5: #5 \\
6: #6 \\
7: #7
}
\begin{document}
\TestArg[Three](Four){Five}(Six)[Seven]
\par\vspace{2\baselineskip}
\TestArg
[Three]
(Four)
{Five}%
(Six)%
[Seven]
\end{document}

This is by design so that (like the optional arguments in amsmath) you do not get mis-interpretation in things like

.... 25\\
[foo] & 26 \\

where [foo] is intended to be the data in the array cell, not an argument to \\

2

With the latest release of xparse, you will find that the b is picked up as you expect. We have refined the treatment of trailing optional arguments such that spaces can be allowed or forbidden:

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\TestArgOne{m O{x}}{%
  1: #1, 2: #2
}
\NewDocumentCommand\TestArgTwo{m!O{x}}{%
  1: #1, 2: #2
}

\begin{document}

\TestArgOne{a}[b]

\TestArgOne{a} [b]

\TestArgTwo{a}[b]

\TestArgTwo{a} [b]

\end{document}

As you will see, \TestArgOne grabs the [b] as an optional argument whether or not there is a space. In contrast, for \TestArgTwo I have used the syntax !O{x}, where the ! means that the optional argument has to follow immediately with no space. This controllable behaviouris useful as depending on the use case, a space may or may not make sense. (The classic example is \\[...], which is used in math mode and where a space or newline before the [ means something very different.)

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.