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}
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
Jack
  • 1,137
  • 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
  • @egreg If you don'y mind explaining, what problem could arise from accepting optional arguments on a new line? – Phelype Oleinik Mar 21 '18 at 23:34
  • @egreg: As I said, I had some long names in my arguments, so for clarity I wanted them each on its own line (at the call!). What speaks against {\empty}? – Jack Mar 21 '18 at 23:38
  • @Jack Well, you can't with optional arguments in brackets, that's all. About {\empty}, this is not empty. – egreg Mar 21 '18 at 23:40
  • 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
  • @egreg: 1) Is this retriction only for optional args in brackets? 2) when or why would I use \empty? Never in arguments? If you make your comment to an answer, then I can accept it. – Jack Mar 21 '18 at 23:46
  • there is no reason to use \empty as the default value of an argument as it just makes things be slower, xparse inserts \empty that then expands to nothing, rather than making the default {} in which case xparse inserts nothing – David Carlisle Mar 21 '18 at 23:54
  • Perhaps worth raising this on the LaTeX-L mailing list: the team have argued back-and-forth about this issue, e.g. what is 'expected', what is consistent (think \item[foo]), etc. – Joseph Wright Mar 22 '18 at 07:14
  • And documenting it in an easy-to-find place would also be helpful. Well, at least it is on SE now:-) – Jack Mar 22 '18 at 07:33

3 Answers3

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.

TeXnician
  • 33,589
egreg
  • 1,121,712
  • 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 07:13
  • @egreg: What signature would this key-val solution have? – Jack Mar 22 '18 at 08: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 08: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 08: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 08: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 \\

David Carlisle
  • 757,742
3

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.)

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036