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
    \do is used everywhere. You should redefine it only very locally. – Ulrike Fischer Sep 12 at 22:12
  • 2
    Avoid \dolistloop and prefer \forlistloop, where you specify a handler. – egreg Sep 12 at 22:14
up vote 11 down vote accepted

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.

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}

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.