How to define this new math mode environment with comma-separated keyval options

environmentskey-valuemacrosmath-mode

My MWE using LuaLaTeX:

\documentclass[oneside,DIV=12]{scrbook}

\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
    \setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{keyval}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \adjintertext custom spacing \intertext macro (https://tex.stackexchange.com/a/280847/228055)
    \MHInternalSyntaxOn
    \newcommand{\adjintertext}[3]% #1=above skip, #2=below skip, #3=text
    {\ifvmode\else\\\@empty\fi
      \noalign{%
        %\penalty\postdisplaypenalty\vskip\belowdisplayskip
        \vskip-\lineskiplimit      % CCS
        \vskip\normallineskiplimit % CCS
        \vskip#1
        \vbox{\normalbaselines
            \ifdim
            \ifdim\@totalleftmargin=\z@
                \linewidth
            \else
                -\maxdimen
            \fi
            =\columnwidth
            \else \parshape\@ne \@totalleftmargin \linewidth
            \fi
        \noindent#3\par}%
        %\penalty\predisplaypenalty\vskip\abovedisplayskip%
        \vskip-\lineskiplimit      % CCS
        \vskip\normallineskiplimit % CCS
        \vskip#2
    }}%
    \MHInternalSyntaxOff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}

\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\adjintertext{0pt}{12pt}{\centering%
  $\begin{alignedat}{2}
     & \text{(i) }   & a &= b, \\
     & \text{(ii) }  & a &< b, \\
     & \text{(iii) } & b &< a.
  \end{alignedat}$%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]

\end{document}

enter image description here

I wanna make a new environment myalignedat to typeset centered and aligned equations like my MWE such that instead of typing

\begin{<display math mode environment>}
    \adjintertext{0pt}{12pt}{\centering%
     \(\begin{alignedat}{2}
     & \text{(i) }   & a &= b, \\
     & \text{(ii) }  & a &< b, \\
     & \text{(iii) } & b &< a.
    \end{alignedat}\)%
}
\end{<display math mode environment>}

I would type the following instead:

\begin{<display math mode environment>}
    \begin{myalignedat}{2}[before skip=0pt, after skip=12pt]
        & \text{(i) }   & a &= b, \\
        & \text{(ii) }  & a &< b, \\
        & \text{(iii) } & b &< a.
    \end{myalignedat}
\end{<display math mode environment>}

similar to how the tcolorbox does it for its environments.

My question is: How can I define such an environment?

I've tried using \newenviron or the environ and xparse packages but to no avail since I have little knowledge about the matter. Thank you.

Best Answer

Original answer

For this to work we have to somehow get the \noalign at a position where TeX finds it (inside of the \begin{myalignedat} definition seems too late for this). Therefore the following uses the env/myalignedat/before and env/myalignedat/after hooks along with some brace trick (\ifnum0=`{ and \ifnum0=`}) to get the \noalign to where we need it. Also we need to reimplement the \adjintertext code in our environment.

Once we have that out of our way the rest is pretty straight forward, we just collect an optional and a mandatory argument and use any key=val implementation that fits our needs (the following uses expkv with the expkv-def key-defining frontend1, but other packages would work as well). Another small adjustment (that one isn't really necessary though) is that the following doesn't use \begin{alignedat}...\end{alignedat} but \alignedat...\endalignedat instead.

Complete code:

\documentclass[oneside,DIV=12]{scrbook}

\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
    \setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \adjintertext custom spacing \intertext macro (https://tex.stackexchange.com/a/280847/228055)
    \MHInternalSyntaxOn
    \newcommand{\adjintertext}[3]% #1=above skip, #2=below skip, #3=text
    {\ifvmode\else\\\@empty\fi
      \noalign{%
        %\penalty\postdisplaypenalty\vskip\belowdisplayskip
        \vskip-\lineskiplimit      % CCS
        \vskip\normallineskiplimit % CCS
        \vskip#1
        \vbox{\normalbaselines
            \ifdim
            \ifdim\@totalleftmargin=\z@
                \linewidth
            \else
                -\maxdimen
            \fi
            =\columnwidth
            \else \parshape\@ne \@totalleftmargin \linewidth
            \fi
        \noindent#3\par}%
        %\penalty\predisplaypenalty\vskip\abovedisplayskip%
        \vskip-\lineskiplimit      % CCS
        \vskip\normallineskiplimit % CCS
        \vskip#2
    }}%
    \MHInternalSyntaxOff
\makeatletter
\usepackage{expkv-def}
\ekvdefinekeys{myalignedat}
  {
     skip before skip = \myalignedat@before
    ,skip after skip  = \myalignedat@after
  }
\AddToHook{env/myalignedat/before}
  {\ifvmode\else\\\@empty\fi\noalign{\ifnum0=`}\fi}
\AddToHook{env/myalignedat/after}
  {\ifnum0=`{\fi}}
\MHInternalSyntaxOn
\newenvironment{myalignedat}[2][]
  {%
    \ekvset{myalignedat}{#1}%
    \vskip\myalignedat@before
    \vbox\bgroup\normalbaselines
      \ifdim
        \ifdim\@totalleftmargin=\z@
          \linewidth
        \else
          -\maxdimen
        \fi
        =\columnwidth
      \else
        \parshap\@ne\@totalleftmargin \linewidth
      \fi
      \noindent
      \centering\(\alignedat{#2}%
  }
  {%
      \endalignedat\)%
    \par\egroup
    \vskip-\lineskiplimit
    \vskip\normallineskiplimit
    \vskip\myalignedat@after
  }
\MHInternalSyntaxOff
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}

\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\begin{myalignedat}[before skip=0pt, after skip=12pt]{2}
   & \text{(i) }   & a &= b, \\
   & \text{(ii) }  & a &< b, \\
   & \text{(iii) } & b &< a.
\end{myalignedat}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]

\end{document}

Output:

enter image description here

1Disclaimer: I'm the package author of expkv and friends


Improved version needing to hack \begin

The above code version couldn't detect whether a \\ was used before it or not, and thus lead to inconsistent vertical spacing. The following fixes that by patching LaTeX's \begin to have another hook. The patch should be fine and not affect any other usage of \begin.

\documentclass[oneside,DIV=12]{scrbook}

\makeatletter
% redefine `\begin` to have another hook. This hook is no generic hook, so we
% have to use `\NewHook{env/#1/evenbeforebefore}` before we can use `\AddToHook`
% on it (for each environment that needs it)
\edef\begin#1%
  {%
    \unexpanded
      {%
        \ifx\protect\relax
        \else
          \expandafter\@gobbletwo
        \fi
      }%
    \noexpand\UseHook{env/#1/evenbeforebefore}%
    \noexpand\protect\expandafter\noexpand\csname begin \endcsname{#1}%
  }
\makeatother

\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
    \setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\makeatletter
\usepackage{expkv-def}
\ekvdefinekeys{myalignedat}
  {
     skip before skip = \myalignedat@before
    ,skip after skip  = \myalignedat@after
  }
\NewHook{env/myalignedat/evenbeforebefore}
\AddToHook{env/myalignedat/evenbeforebefore}
  {\ifvmode\else\\\@empty\fi\noalign{\ifnum0=`}\fi}
\AddToHook{env/myalignedat/after}
  {\ifnum0=`{\fi}}
\MHInternalSyntaxOn
\newenvironment{myalignedat}[2][]
  {%
    \ekvset{myalignedat}{#1}%
    \vskip\myalignedat@before
    \vbox\bgroup\normalbaselines
      \ifdim
        \ifdim\@totalleftmargin=\z@
          \linewidth
        \else
          -\maxdimen
        \fi
        =\columnwidth
      \else
        \parshap\@ne\@totalleftmargin \linewidth
      \fi
      \noindent
      \centering\(\alignedat{#2}%
  }
  {%
      \endalignedat\)%
    \par\egroup
    \vskip-\lineskiplimit
    \vskip\normallineskiplimit
    \vskip\myalignedat@after
  }
\MHInternalSyntaxOff
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}

\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\) with much text following it}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\begin{myalignedat}[before skip=0pt, after skip=0pt]{2}
   & \text{(i) }   & a &= b, \\
   & \text{(ii) }  & a &< b, \\
   & \text{(iii) } & b &< a.
\end{myalignedat}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]

\end{document}

Pseudo-environment version

This version uses a fully expandable macro to start a pseudo-environment. It doesn't require any hacking of standard LaTeX macros, but it still uses the brace hacks. It expands to \noalign directly and can then use unexpandable argument grabbing and key=value parsing.

The environment is started by \beginmyalignedat[<key=value>]{<alignedat-arg>} and ended by \stopmyalignedat.

Complete code:

\documentclass[oneside,DIV=12]{scrbook}

\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
    \setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{keyval}
\usepackage{lipsum}

\usepackage{expkv-def}

\makeatletter
\ekvdefinekeys{myalignedat}
  {
     skip before skip = \myalignedat@before
    ,skip after skip  = \myalignedat@after
  }
\MHInternalSyntaxOn
\newcommand*\beginmyalignedat
  {%
    \ifvmode\else\\\@empty\fi
    \noalign{\ifnum0=`}\fi
      \myalignedat@
  }
\newcommand\myalignedat@[2][]
  {%
      \ekvset{myalignedat}{#1}%
      \vskip-\lineskiplimit
      \vskip\normallineskiplimit
      \vskip\myalignedat@before
      \vbox\bgroup
        \normalbaselines
        \ifdim
          \ifdim\@totalleftmargin=\z@
            \linewidth
          \else
            -\maxdimen
          \fi
          =\columnwidth
        \else
          \parshape\@ne\@totalleftmargin\linewidth
        \fi
        \noindent
        \centering
        $\begin{alignedat}{#2}%
  }
\newcommand\stopmyalignedat
  {%
        \end{alignedat}$%
        \par
        \vskip-\lineskiplimit
        \vskip\normallineskiplimit
        \vskip\myalignedat@after
      \egroup
    \ifnum0=`{\fi}%
  }
\MHInternalSyntaxOff
\makeatother

\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}

\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\beginmyalignedat[after skip=12pt]{2}
   & \text{(i) }   & a &= b, \\
   & \text{(ii) }  & a &< b, \\
   & \text{(iii) } & b &< a.
\stopmyalignedat
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]

\end{document}

Normal macro version

A macro variant that just grabs the arguments in an expandable way, uses an expandable key=value interface, and hence "directly" expands to \adjintertext, such that it behaves as if \adjintertext was used like in your code. An alternative version could use the same brace trick like the other solutions to use non-expandable argument grabbing and key=value solutions.

The syntax is:

\myalignedat[<key=val>]{<alignedat-arg>}{<alignedat-content>}

Complete code:

\documentclass[oneside,DIV=12]{scrbook}

\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
    \setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{keyval}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \adjintertext custom spacing \intertext macro (https://tex.stackexchange.com/a/280847/228055)
    \MHInternalSyntaxOn
    \newcommand{\adjintertext}[3]% #1=above skip, #2=below skip, #3=text
    {\ifvmode\else\\\@empty\fi
      \noalign{%
        %\penalty\postdisplaypenalty\vskip\belowdisplayskip
        \vskip-\lineskiplimit      % CCS
        \vskip\normallineskiplimit % CCS
        \vskip#1
        \vbox{\normalbaselines
            \ifdim
            \ifdim\@totalleftmargin=\z@
                \linewidth
            \else
                -\maxdimen
            \fi
            =\columnwidth
            \else \parshape\@ne \@totalleftmargin \linewidth
            \fi
        \noindent#3\par}%
        %\penalty\predisplaypenalty\vskip\abovedisplayskip%
        \vskip-\lineskiplimit      % CCS
        \vskip\normallineskiplimit % CCS
        \vskip#2
    }}%
    \MHInternalSyntaxOff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\usepackage{expkv-cs}

% grabbing an optional argument expandable
\NewExpandableDocumentCommand\myalignedat{O{} m m}
  {%
    % forward all arguments to \myalignedatKV
    \myalignedatKV{#1}{#2}{#3}%
  }
% splitting the first argument into two arguments based on a key=value interface
\ekvcSplitAndForward\myalignedatKV\myalignedatDO
  {
     before skip=0pt
    ,after  skip=0pt
  }
% grabbing all the arguments and setting the output
\newcommand\myalignedatDO[4]
  {%
    \adjintertext{#1}{#2}
      {%
        \centering
        $%
        \begin{alignedat}{#3}
          #4%
        \end{alignedat}%
        $%
      }%
  }

\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}

\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\myalignedat[after skip=12pt]{2}
  {
     & \text{(i) }   & a &= b, \\
     & \text{(ii) }  & a &< b, \\
     & \text{(iii) } & b &< a.
  }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]

\end{document}