6

If there is just a short line of text before an equation environment, then the short skips (\abovedisplayshortskip and \belowdisplayshortskip will be used.

But for amsmath environments such as align it will only ever insert the regular \abovedisplayskip and \belowdisplayskip, even if the preceding line is very short

Can we redefine align and friends such that they behave similar to equation and automatically insert short skips where applicable?

\documentclass{article}
\usepackage{amsmath}
\begin{document}%
If there is just a short line of text before an \verb|equation| environment, then the short skips (\verb|\abovedisplayshortskip| and \verb|\belowdisplayshortskip|) will be used%
\begin{equation}%
 a=b
\end{equation}%
But for amsmath environments such as \verb|align| it will only ever insert the regular \verb|\abovedisplayskip| and \verb|\belowdisplayskip|, even if the preceding line is very short
\begin{align}%
 a&=b\\
 a&=b
\end{align}%
Can we redefine \verb|align| and friends such that they behave similar to \verb|equation| and automatically insert short skips where applicable?\\[\baselineskip]
\begin{tabular}{ll}%
 \verb|\abovedisplayshortskip| & \the\abovedisplayshortskip\\
 \verb|\abovedisplayskip|      & \the\abovedisplayskip\\
 \verb|\belowdisplayshortskip| & \the\belowdisplayshortskip\\
 \verb|\belowdisplayskip|      & \the\belowdisplayskip
\end{tabular}%
\end{document}%

compiled document

  • I am clueless on this, but strongly suspect that if it could have been done, it would have been done. – Steven B. Segletes Aug 10 '17 at 18:01
  • That’s an old problem: \SwapAboveDisplaySkip, from the mathtools package, tries to address it. – GuM Aug 10 '17 at 18:04
  • This question looks like an exact duplicate of Vertical spacing of equations using align environment following short texts. – GuM Aug 10 '17 at 18:08
  • 1
    @GuM that is a manual solution. I've seen an unofficial reimplementation of gather that had this feature. For the OP, note that align always start at the left edge of the text block, and equation starts in the middle, so it is vastly different constructions. If it is align* I often replace it with equation*+aligned, this combo has the space saving feature, though only in comparison to the overall width of the aligned. – daleif Aug 10 '17 at 18:17
  • Of course I expected it would not be easy, but still I am disappointed that there seems to be no automatic solution (not even one of the tradeoff type "we can kinda implement it, but that may break feature X") The inconsistent spacing annoys me. Do people mix align and equation and just live with the weird spaces? If I can't find a solution then I am tempted to use align for everything (even single line equations) just to get the spacing consistent. Would that be recommended? Anyway, I'll try to develop my own shortskip hack tomorrow and post it here so you can tell me how hackybad it is. ;) – wea0 Aug 12 '17 at 1:50
4

OK, I made a thing. It seems to work with align & co. I have no idea what other things it might break. Any thoughts?

\documentclass{article}
\usepackage[tbtags]{mathtools}
\usepackage{regexpatch}
\makeatletter
% a new glue to store the amount we need to modify at the end of the display
\newskip\belowdisplayskip@mod
% a new box to store the left-hand side of the first line inside a split environment
\newbox\split@firstline@LHS
% redefine \start@align to incorporate a call to the check whether the preceding line is short enough
\xpatchcmd{\start@align}{%
    \collect@body\next@
}{%
    \collect@body\next@
    \shortskip@check
}{}{\PackageWarningNoLine{methtools}{Failed to patch \protect\start@align}}
% redefine \endalign to correct the belowskip
\xpatchcmd{\endalign}{%
    $$%
}{%
    $$%
    \vskip\belowdisplayskip@mod
    \global\belowdisplayskip@mod\z@
    \noindent
}{}{\PackageWarningNoLine{methtools}{Failed to patch \protect\endalign}}
% Redefine \intertext@ to correct the belowskip. How we can patch this depends on whether mathtools has been loaded, hence we defer this action until \begin{document}.
\AtBeginDocument{%
    \@ifpackageloaded{mathtools}{\MHInternalSyntaxOn}{\makeatletter}%
    \xpatchcmd{\intertext@}{%
        \penalty\postdisplaypenalty\vskip\belowdisplayskip
    }{%
        \penalty\postdisplaypenalty\vskip\belowdisplayskip
        \vskip\belowdisplayskip@mod
        \global\belowdisplayskip@mod\z@
    }{}{\PackageWarningNoLine{methtools}{Failed to patch \protect\intertext@}}
    \@ifpackageloaded{mathtools}{\MHInternalSyntaxOff}{\makeatother}%
}
% There is some weird behavior if split is used and the "tbtags" option is active. The first line of the split environment seems to be a dummy line set without any width on the left-hand side. Since shortskips need to be determined depending on the width of the first line, many split environments would erroneously be detected as qualifying for shortskips. To work around this we modify \insplit@ to explicitly measure the width of the left-hand side of the first line and further modify \rendsplit@ to insert an empty box of said width.
\xpatchcmd{\insplit@}{% Patching a command containing ## seems to require regexpatch's xpatchcmd.
    \ialign\bgroup
        \hfil
        \strut@
        $\m@th\displaystyle{##}$%
}{%
    \setbox\split@firstline@LHS\box\voidb@x
    \ialign\bgroup
        \hfil
        \strut@
        \setbox\@tempboxa\hbox{$\m@th\displaystyle{##}$}\unhcopy\@tempboxa
        \ifvoid\split@firstline@LHS
            \global\setbox\split@firstline@LHS\box\@tempboxa
        \fi
}{}{\PackageWarningNoLine{methtools}{Failed to patch \protect\insplit@}}
\xpatchcmd{\rendsplit@}{%
    \global\@tempcnta\column@
}{%
    \global\@tempcnta\column@
    \hbox to\wd\split@firstline@LHS{}%
}{}{\PackageWarningNoLine{methtools}{Failed to patch \protect\rendsplit@}}
% check whether shortskips should be used; if yes, immediately account for the aboveskip and save belowskip for later
\def\shortskip@check{%
    \ifmeasuring@\else
        \noalign{\shortskip@calc@shift@lc}%
        \ifdim\dimexpr\eqnshift@+\@tempdimb\relax > \predisplaysize
            \noalign{\vskip\glueexpr\abovedisplayshortskip-\abovedisplayskip\relax}%
            \noalign{\global\belowdisplayskip@mod=\glueexpr\belowdisplayshortskip-\belowdisplayskip\relax}%
        \fi
    \fi
}
% first helper function to calculate how much the first equation row is indented. amsmath documentation:
%    \cs{@tempdimb} will be the ``indentation'' of leftmost end of
%    text, ignoring the \cs{tabskip} glue, i.e., it will be the sum of
%    the maximum widths of any fields to the left of the first
%    non-empty field, plus whatever empty space there is at the
%    beginning of the first non-empty field.
\def\shortskip@calc@shift@lc{% based on amsmath \x@calc@shift@lc
    \row@\@ne
    \column@\z@
    \@tempdima\z@
    \@tempdimb\z@
    \edef\@tempb{\fieldlengths@\row@}%
    \@for\@tempa:=\@tempb\do{%
        \advance\column@\@ne
        \shortskip@lcalc@width
    }%
}
% second helper function to calculate how much the first equation row is indented. amsmath documentation:
%    This macro calculates the ``indentation'' of the current row, as
%    defined above under the description of \cs{x@calc@shift@lc}.
%    This macro is called for each field of the current line, with
%    \cs{@tempa} set to the width of the current field.  Ideally, the
%    loop enclosing \cs{x@lcalc@width} would terminate as soon as
%    \cs{@tempa} is non-zero, but that would be a bit tricky to
%    arrange.  Instead, we use \cs{@tempdima} as a flag to signal when
%    we've encountered the first non-empty field.
\def\shortskip@lcalc@width{% based on amsmath \x@lcalc@width
    \ifdim\@tempdima = \z@
        \ifdim\@tempa > \z@
            \@tempdima\p@
            \ifodd\column@
                \advance\@tempdimb \maxcol@width\column@
                \global\advance\@tempdimb-\@tempa
            \fi
            \count@\column@
            \advance\count@\m@ne
            \divide\count@\tw@
            \advance\@tempcnta-\count@
            \advance\@tempcntb-\count@
        \else
            \global\advance\@tempdimb \maxcol@width\column@\relax
        \fi
    \fi
}
\makeatother
\begin{document}%
If there is just a short line of text before an \verb|equation| environment, then the short skips (\verb|\abovedisplayshortskip| and \verb|\belowdisplayshortskip|) will be used%
\begin{equation}%
  a=b
\end{equation}%
My modifed \verb|align| will now also insert short skips if the preceding line is short enough, such as in this example%
\begin{align}%
  a&=b\\
  a&=b
\end{align}%
It should depend only on the first equation; here shortskips are used:\\short%
\begin{align}%
                        a&=b\\
  aaaaaaaaaaaaaaaaaaaaaaa&=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
\end{align}%
but not here, since the first equation is too close to the text (less than 2em):\\regular%
\begin{align}%
  a&=b\\
  a&=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
\end{align}%
It should also work with multiple columns\\short%
\begin{align}%
        a&=b      &      a&=b       &      a&=b\\
  aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb
\end{align}%
It should also work with multiple columns, now the other way round\\regular%
\begin{align}%
  aaaaaa&=bbbbbbb &       a&=b      &      a&=b\\
       a&=b       & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb
\end{align}%
Let's see what happens if the first entry in the first column is empty\\short%
\begin{align}%
        &         & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb\\
  aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb
\end{align}%
%
\newpage\noindent
%
It has also redefined the friends such has \verb|align*|\\short%
\begin{align*}%
       a&=b       & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb\\
  aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb
\end{align*}%
It has also redefined the friends such has \verb|align*|\\regular%
\begin{align*}%
  aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb\\
       a&=b       & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb
\end{align*}%
What about \verb|alignat|? Seems to work as well\\short%
\begin{alignat}{3}%
  aaaaaa&=bbbbbbb &\quad aaaaaa&=bbbbbbb &\quad aaaaaa&=bbbbbbb\\
  aaaaaa&=bbbbbbb &      aaaaaa&=bbbbbbb &      aaaaaa&=bbbbbbb
\end{alignat}%
What about \verb|alignat|? Seems to work as well\\regular%
\begin{alignat}{3}%
  aaaaaa&=bbbbbbb &       aaaaaa&=bbbbbbb &       aaaaaa&=bbbbbbb\\
  aaaaaa&=bbbbbbb &\qquad aaaaaa&=bbbbbbb &\qquad aaaaaa&=bbbbbbb
\end{alignat}%
There's also \verb|flalign|. Even that one can become short if first entry is missing\\short%
\begin{flalign}%
        &         & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb\\
  aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb & aaaaaa&=bbbbbbb
\end{flalign}%
It works. Yay. What do you think?\\[\baselineskip]
\begin{tabular}{@{}ll}%
  \verb|\abovedisplayshortskip| & \the\abovedisplayshortskip\\
  \verb|\abovedisplayskip|      & \the\abovedisplayskip\\
  \verb|\belowdisplayshortskip| & \the\belowdisplayshortskip\\
  \verb|\belowdisplayskip|      & \the\belowdisplayskip
\end{tabular}%
%
\newpage\noindent
%
Now let's use \verb|split| inside \verb|align|, where the preceding line is\\short%
\begin{align}%
\begin{split}%
  aaaaaaaaaaa&=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\\
             &=ddddddddddddddddddddddddddddddddddd
\end{split}%
\end{align}%
Now let's use \verb|split| inside \verb|align|, where the preceding line is\\not so short%
\begin{align}%
\begin{split}%
  aaaaaaaaaaa&=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\\
             &=ddddddddddddddddddddddddddddddddddd
\end{split}%
\end{align}%
Now let's use \verb|split| inside \verb|align|, but switch it around\\still short enough%
\begin{align}%
\begin{split}%
             &=ddddddddddddddddddddddddddddddddddd\\
  aaaaaaaaaaa&=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
\end{split}%
\end{align}%
It works. Yay. What do you think?
\end{document}%

| improve this answer | |
  • Obervations so far: 1. The split behavior seems worse than expected. Looks as if the width of the left hand side of split can be calculated as zero and thus split equations get moved up way too oftern. 2. If two equations are separated by intertext and the first one triggers shortskips, then the belowdisplayshortskip is enforced after the second equation, not before the intertext. – wea0 Aug 13 '17 at 12:22
  • The second problem can easily be solved by patching \intertext@, but only when using the standard amsmath intertext. If mathtools is loaded then intertext is modified and becomes something that seems completely immune to patching, even with regexpatch. – wea0 Aug 13 '17 at 23:49
  • I updated this answer with improved code. It is much more readable and patches the commands instead of overwriting them. It also treats intertext now. Major open problem are: 1. The width calculation gives incorrect results for split environments inside align. 2. Patching \intertext@ fails if mathtools is loaded (unless one restores the older amsmath definition using the "original-intertext" option). Can anyone provide some input how to solve these problems please? – wea0 Aug 16 '17 at 21:54
  • I updated it again. The weird behavior with split was due to the tbtags option for amsmath which I usually use. If that one is active, then the split seems to take up two "lines" in the align instead of just one as usual. The first one is some dummy line where the left-hand side has zero width, which messes up the shortskip calculation. I now try to measure the true width of the left-hand side of the first row inside split and insert an empty box to simulate that width also on the dummy line. Still don't know how to patch \intertext@. Any ideas on that? Any comments on the code? – wea0 Aug 19 '17 at 12:40
  • Yet another update. Now it also works with mathtools' version of \intertext@. I haven't found any other bugs, so I'll accept this answer for now. – wea0 Aug 29 '17 at 23:29
1

An automatic solution doesn't exist, as far as I know, but you can use the \useshortskip command from nccmath. However, it replaces only \abovedisplayskip with \abovedisplayshortskip and doesn't replace \belowdisplayskip. If you want to, you can patch nccmath , and more precisely the \NCC@ignorepar command

Naturally, one could also add to nccmath a new \useshortskips command which operates on both skips and keep the original \useshortskip as is, but I'm not sure it's worth the effort.

\documentclass{article}
\usepackage{amsmath, nccmath}
\usepackage{xpatch}
\makeatletter
\xpatchcmd{\NCC@ignorepar}{%
\global}{%
    \belowdisplayskip\belowdisplayshortskip
    \global}{}{}
\makeatother

\begin{document}%
If there is just a short line of text before an \verb|equation| environment, then the short skips (\verb|\abovedisplayshortskip| and \verb|\belowdisplayshortskip|) will be used%
\begin{equation}%
 a=b
\end{equation}%
But for amsmath environments such as \verb|align| it will only ever insert the regular \verb|\abovedisplayskip| and \verb|\belowdisplayskip|, even if the preceding line is very short
\begin{align}%
 a&=b\\
 a&=b
\end{align}%
Can we redefine \verb|align| and friends such that they behave similar to \verb|equation| and automatically insert short skips where applicable?\\[\baselineskip]
\begin{tabular}{ll}%
 \verb|\abovedisplayshortskip| & \the\abovedisplayshortskip\\
 \verb|\abovedisplayskip| & \the\abovedisplayskip\\
 \verb|\belowdisplayshortskip| & \the\belowdisplayshortskip\\
 \verb|\belowdisplayskip| & \the\belowdisplayskip
\end{tabular}%

But for amsmath environments such as \verb|align| it will only ever insert the regular \verb|\abovedisplayskip| and \verb|\belowdisplayskip|, even if the preceding line is very short\useshortskip
\begin{align}%
 a&=b\\
 a&=b
\end{align}%
Can we redefine \verb|align| and friends such that they behave similar to \verb|equation| and automatically insert short skips where applicable?

\end{document}% 

enter image description here

| improve this answer | |

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.