1

I'm trying to build a macro that can be used to place an arbitrary number of items in a list

Currently my macro looks like this

\newcommand\presentation[6]{
  \begin{minipage}[t]{\dimexpr(\linewidth) - 3em}
    \textbf{#1} \\*
    #2 \smallskip
    \begin{itemize}
        \setlength{\itemindent}{-0.15in}
        \item  #3 -- #4 #5 #6
    \end{itemize}
  \end{minipage} \bigskip\\*
}

if 10 input arguments are passed to the macro, I'd like it to do this

\newcommand\presentation[6]{
  \begin{minipage}[t]{\dimexpr(\linewidth) - 3em}
    \textbf{#1} \\*
    #2 \smallskip
    \begin{itemize}
        \setlength{\itemindent}{-0.15in}
        \item  #3 -- #4 #5 #6
        \item  #7 -- #8 #9 #10
    \end{itemize}
  \end{minipage} \bigskip\\*
}

And so on with additional input variables

Based on this I tried the following but I doesn't seem to work ...

\makeatletter
\newcommand{\present}[6]{%
      \begin{minipage}[t]{\dimexpr(\linewidth) - 3em}
            \textbf{#1} \\*
            #2 \smallskip
            \begin{itemize}
                \setlength{\itemindent}{-0.15in}
                \item  #3 -- #4 #5 #6
}

\newcommand{\checknextarg}{\@ifnextchar\bgroup{\gobblenextarg}{ 
    \end{itemize} 
    \end{minipage} 
    \bigskip\\*}
}

\newcommand{\gobblenextarg}[4]{ \item  #1\@ifnextchar\bgroup{\gobblenextarg} -- #2\@ifnextchar\bgroup{\gobblenextarg} #3\@ifnextchar\bgroup{\gobblenextarg} #4\@ifnextchar\bgroup{\gobblenextarg}
    {       
        \end{itemize} 
        \end{minipage} 
        \bigskip\\*
    }
}
\makeatother
agf1997
  • 251
  • 1
  • 8

2 Answers2

2

Your attempt \present doesn't even call \checknextarg... In order to simplify the code, I'll assume the number of arguments is of the form 4n + 2, where n is a positive integer. Otherwise, more tests are needed.

Note:

  • \dimexpr doesn't need parentheses where you put them.

  • \bigskip\\* doesn't make much sense because \bigskip itself is a legitimate breakpoint in most cases (it is one as long as what precedes it is a non-discardable item of the vertical list: a box, a whatsit, a mark or an insertion—see TeXbook p. 110, or the very short introduction below).

  • Also, beware of introducing unwanted spaces.

Regarding your precise problem, you can do something like this:

\documentclass{article}

\makeatletter

\newcommand{\presentation}[2]{%
  \begin{minipage}[t]{\dimexpr \linewidth - 3em}
    \textbf{#1}\\*
    #2\smallskip
    \begin{itemize}
      \setlength{\itemindent}{-0.15in}%
      \checknexttoken
}

\newcommand{\checknexttoken}{\@ifnextchar\bgroup{\onemoretime}{\finished}}
\newcommand{\onemoretime}[4]{%
  \item #1 -- #2 #3 #4%
  \@ifnextchar\bgroup{\onemoretime}{\finished}%
}

\newcommand*{\finished}{%
  \end{itemize}
  \end{minipage}
  \par
  % \nopagebreak % uncomment if you want to prevent page breaks here
  \bigskip
}

\makeatother

\begin{document}

\presentation{Title}{Items:}{first}{second}{third}{fourth}

\presentation{Title}{Items:}
  {first}{second}{third}{fourth}
  {fifth}{seventh}{eighth}{ninth}

\presentation{Title}{Items:}
  {first}{second}{third}{fourth}
  {fifth}{seventh}{eighth}{ninth}
  {tenth}{eleventh}{twelfth}{thirteenth}

\end{document}

Screenshot

In case you need to have an opening brace at the point where you would like the argument grabbing to stop, just insert a \relax in your text before the brace so as to prevent a “next round” from being triggered.

Quick notes on page breaking

When you finish a paragraph in outer vertical mode, each of its lines forms a horizontal box which is therefore a non-discardable item of the main vertical list. Each such line is followed in the main vertical list by vertical material that has “migrated out” (from \vadjust, \insert, \mark...), penalties (for club or widow lines, etc.) and automatically-inserted interline glue. Glue items and penalties are both potential breakpoints, under certain conditions. In the frequent case where a horizontal box representing a paragraph line is immediately followed by a glue item in the main vertical list (glue which could be obtained for instance from a \bigskip immediately following the paragraph, or from interline glue automatically added between two consecutive lines), because the box is by definition non-discardable, this glue item is a legitimate breakpoint (i.e., a page break may take place right after the box and would normally discard the glue item: glue items are discardable ones).

\nopagebreak inserts a penalty, which is a discardable item, therefore a \nopagebreak immediately preceding something like \bigskip that inserts a glue item in the main vertical list, prevents the glue item from being a legitimate breakpoint. \par just switches to vertical mode. It doesn't insert anything like a glue item, a box or a penalty by itself.

frougon
  • 24,283
  • 1
  • 32
  • 55
  • Thank you so much! This is a super useful answer – agf1997 Jul 07 '19 at 19:57
  • 1
    Glad it helped. I added some precisions regarding page breaking. – frougon Jul 07 '19 at 20:10
  • Thanks again ... I'm not sure I completely get the page brake situation with \bigskip When I don't include \\* my second \presentation gets to the right of my first. When I include \\* it gets typeset below. – agf1997 Jul 07 '19 at 20:22
  • 1
    Yes, \\ does cause a line break. If you remove it, no line break occurs. What I was trying to say is that the star form doesn't make sense after a \bigskip, because the point of using \\* as opposed to \\ is that \\* normally prevents page breaking. But if what precedes is \bigskip, the \\* won't prevent page breaking to occur at the \bigskip, so the star is only giving a false sense of security. To avoid unwanted page breaks, one has to make sure there is no legal breakpoint. Adding \\* after a breakpoint doesn't prevent anything. – frougon Jul 07 '19 at 20:27
  • 1
    This is a delicate matter, read chapter 15 of the TeXbook if you want all this to be clear. – frougon Jul 07 '19 at 20:28
  • Ahhh ... this make sense ... it wasn't clear to me that you were referring to the start form only. – agf1997 Jul 07 '19 at 20:52
2

I suggest a different approach, with an environment:

\documentclass{article}

\newenvironment{presentation}[2]
 {\par\noindent\begin{minipage}[t]{\dimexpr\linewidth-3em}
  \textbf{#1}\\#2\smallskip
  \begin{itemize}\setlength{\itemindent}{-0.15in}}
 {\end{itemize}\end{minipage}\par\addvspace{2\bigskipamount}}

\newcommand{\present}[4]{\item #1 -- #2 #3 #4}

\begin{document}

\begin{presentation}{Title}{Items:}
\present{first}{second}{third}{fourth}
\present{fifth}{seventh}{eighth}{ninth}
\end{presentation}

\begin{presentation}{Title}{Items:}
\present{first}{second}{third}{fourth}
\end{presentation}

\end{document}

This has the advantage of being more “free form”: it doesn't matter if you use blank lines between \present lines, so

\begin{presentation}{Title}{Items:}
\present{first}{second}{third}{fourth}

\present{fifth}{seventh}{eighth}{ninth}
\end{presentation}

would typeset the same.

Should you insist on the “variable number of arguments” approach:

\documentclass{article}

\newcommand{\presentation}[2]{
  \par\noindent\begin{minipage}[t]{\dimexpr\linewidth-3em}
  \textbf{#1}\\#2\smallskip
  \begin{itemize}\setlength{\itemindent}{-0.15in}
  \present
}

\makeatletter
\newcommand{\present}[4]{
  \item #1 -- #2 #3 #4
  \@ifnextchar\bgroup{\present}{\end{itemize}\end{minipage}\par\addvspace{2\bigskipamount}}%
}
\makeatother

\begin{document}

\presentation{Title}{Items:}
  {first}{second}{third}{fourth}
  {fifth}{seventh}{eighth}{ninth}

\presentation{Title}{Items:}
  {first}{second}{third}{fourth}

\end{document}

The output is the same.

enter image description here

egreg
  • 1,121,712