Alignment tab character inside a starred command within align
TL;DR
Simple solution: use a more robust check for the *-variant.
\documentclass{article}
\usepackage{amsmath}
\NewDocumentCommand{\mylinebreak}{s}{%
\IfBooleanTF{#1}{&x\\}{&x&=y\\}%
}
\begin{document}
\begin{align*}
a&=b\mylinebreak
&=c.\\
a&=b\mylinebreak*
&=c.\\
\end{align*}
\end{document}
I added something in order to better see that the choices are made correctly.
If you're running a version of LaTeX prior to the 2020-10-01 release, you also need \usepackage{xparse}
.
Analysis of the issue
Let's see what happens: \mylinebreak
becomes
\@ifstar\@mylinebreak\@@mylinebreak
According to the definition of \@ifstar
by amsmath
\def\@ifstar#1#2{\new@ifnextchar *{\def\reserved@a*{#1}\reserved@a}{#2}}
the above becomes
\new@ifnextchar*{\def\reserved@a{\@mylinebreak}\reserved@a}{\@@mylinebreak}
Let's see what \new@ifnextchar
does:
\long\def\new@ifnextchar#1#2#3{%
\let\reserved@d= #1%
\def\reserved@a{#2}\def\reserved@b{#3}%
\futurelet\@let@token\new@ifnch
}
so the expansions continues as (newlines just for reading convenience)
\let\reserved@d= *
\def\reserved@a{\def\reserved@a*{\@mylinebreak}\reserved@a}
\def\reserved@b{\@@mylinebreak}
\futurelet\@let@token\new@ifnch &
The trailing &
comes from the fact that the newline after \mylinebreak
is ignored during tokenization. Unfortunately, we're in an alignment, so as soon as TeX scans &
, it inserts the v part of the template. And, indeed, the error message with a high value of \errorcontextlines
reads
! Misplaced alignment tab character &.
\@@mylinebreak ->&
&\\
<to be read again>
}
<template> }
$}\ifmeasuring@ \savefieldlength@ \fi \set@field \hfil \endtempl...
<argument> a&=b\mylinebreak &
=c.
and \@let@token
is set to }
. This is confirmed by injecting \show\@let@token
at the beginning of \new@ifnch
:
\@let@token=end-group character }
OK, let's go a step forward: the expansion of \new@ifnch
yields
\ifx@let@token\reserved@d\let\reserved@b\reserved@a\fi\reserved@b
In our case, the \ifx
test returns false, so we remain with
\reserved@b}&
and this breaks, because \reserved@b
becomes \@@mylinebreak
.
You are defining \\
to look ahead for a *
(incidentally the standard command already looks ahead for a *
) but in doing this it "sees" the &
and the next cell starts before the previous row is ended by inserting the original \\
The definition in amsmath
is
\def\math@cr{\relax\iffalse{\fi\ifnum0=`}\fi
\@ifstar{\global\@eqpen\@M\math@cr@}%
{\global\@eqpen
\ifnum\dspbrk@lvl <\z@ \interdisplaylinepenalty
\else -\@getpen\dspbrk@lvl \fi
\math@cr@}}
\def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}}
\def\math@cr@@[#1]{\ifnum0=`{\fi \iffalse}\fi\math@cr@@@
\noalign{\vskip#1\relax}}
The special \relax\iffalse{\fi\ifnum0=`}\fi
is designed to avoid this issue by preventing the halign template ending a cell at this point.
Note that it is already using \@ifstar
to define \\*
to prevent a page break at this point.
It is not clear what is the intention of using &\\
as the behaviour of that construct is the same as \\
as the empty cells do not do anything in almost all circumstances.