12

The itemize list below works fine. However, if I attempt to move the

\renewcommand*{\do}[1]{\item #1.}

outside of the itemize, it results in a

Something's wrong--perhaps a missing \item

Questions:

  • Why does moving the \renewcommand*{\do}[1]{\item #1.} to the preamble result in the error
  • How does having the \renewcommand there prevent this error?

Code:

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\CSVList}{}
\listadd{\CSVList}{a}
\listadd{\CSVList}{b}
\listadd{\CSVList}{c}

\renewcommand*{\do}[1]{\item #1.}%

\begin{document}
    \begin{itemize}
        \renewcommand*{\do}[1]{\item #1.}%
        \dolistloop{\CSVList}%
    \end{itemize}

%% -------------------- This results in the "missing \item" error.
%    \begin{itemize}
%        \dolistloop{\CSVList}%
%    \end{itemize}
\end{document}
2
  • 2
    \do is used everywhere. You should redefine it only very locally. Commented Sep 12, 2018 at 22:12
  • 2
    Avoid \dolistloop and prefer \forlistloop, where you specify a handler.
    – egreg
    Commented Sep 12, 2018 at 22:14

2 Answers 2

11

Because \do is used at the \begin{document} by the LaTeX kernel to make the \@onlypreamble commands throw an error when used outside the preamble.

The definition of \document contains:

\gdef\do##1{\global\let ##1\@notprerr}%
\@preamblecmds
\global\let \@nodocument \relax
\global\let\do\noexpand % <-- this causes the problem

so \do becomes \noexpand right after \begin{document}, so your \newcommand in the preamble has no effect.

If you put the \renewcommand inside the itemize, when the environment group ends, the previous definition of \do (\noexpand) is restored and this raises the missing \item.

If you move the \renewcommand outside the itemize, its new definition is kept until the second itemize and everything works fine.

5

Try this:

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\CSVList}{}
\listadd{\CSVList}{a}
\listadd{\CSVList}{b}
\listadd{\CSVList}{c}

\renewcommand*{\do}[1]{\item #1.}%

\begin{document}
    \begin{itemize}
        \renewcommand*{\do}[1]{\item #1.}%
        \dolistloop{\CSVList}%
    \end{itemize}

    \begin{itemize}
\show\do
        \dolistloop{\CSVList}%
    \end{itemize}
\end{document}

The console will show

> \do=\noexpand.
l.18 \show\do

Thus your \do{a} will do

\noexpand\item a

and the output will be, after the “missing \item” error, just

abc

Much better is to define a specific handler:

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\CSVList}{}
\listadd{\CSVList}{a}
\listadd{\CSVList}{b}
\listadd{\CSVList}{c}

\newcommand*{\doCSV}[1]{\item #1.}

\begin{document}

\begin{itemize}
\forlistloop{\doCSV}{\CSVList}
\end{itemize}

\end{document}

I have removed all superfluous % characters.

A different implementation, where the handler is built on the fly; the current item in the list is denoted by #1.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\addtolist}{mm}
 {
  \clist_if_exist:cF { l_grill_list_#1_clist }
   {
    \clist_new:c { l_grill_list_#1_clist }
   }
  \clist_map_inline:nn { #2 }
   {
    \clist_put_right:cn { l_grill_list_#1_clist } { ##1 }
   }
 }
\NewDocumentCommand{\uselist}{mm}
 {
  \cs_set_protected:Nn \__grill_uselist:n { #2 }
  \clist_map_function:cN { l_grill_list_#1_clist } \__grill_uselist:n
 }
\ExplSyntaxOff

\addtolist{CSVList}{a}
\addtolist{CSVList}{b,c}

\begin{document}

\begin{itemize}
\uselist{CSVList}{\item #1.}
\end{itemize}

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .