13

Is it possible to capture the contents to the end-of-line as a macro argument?

For example, consider the following naive visualization:

enter image description here

\documentclass{article}
\newenvironment{myitemize}
  {\def\item{\textbullet\ }}
  {}
\begin{document}
\begin{myitemize}
  \item First
  \item Second
  \item Third
  \item Last
\end{myitemize}
\end{document}

I'd like to define \item using something like

\def\item#1\par{<do stuff with #1>}

but this requires an empty line between items to pick up \par:

enter image description here

\documentclass{article}
\newenvironment{myitemize}
  {\def\item##1\par{\textbullet\ ##1\par}}
  {}
\begin{document}
\begin{myitemize}
  \item First

  \item Second

  \item Third

  \item Last

\end{myitemize}
\end{document}

Looking at \obeylines in latex.ltx I thought this may have something to do with ^^M, but I'm not sure how I can incorporate that into the definition of \item.

  • Related, but not answered explicitly: Is it possible to scan until the end of the current \item – Werner Aug 6 '13 at 18:12
  • You have this in enumitem package, with itemize*. – Manuel Jan 21 '16 at 19:05
  • @Manuel: I'm referring to the general use-case of capturing \items to do something with them. – Werner Jan 21 '16 at 19:07
  • Well, itemize* can be configured, it's enumitem's power. But of course, if you grab them with a macro not bounded to a list you can do much more things. – Manuel Jan 21 '16 at 19:27
12

You can use environ:

\documentclass{article}
\usepackage{environ}

\makeatletter
\NewEnviron{myitemize}{%
  \def\item##1\item{\dosomething{##1}}%
  \expandafter\@empty\BODY\item}

\newcommand{\dosomething}[1]{%
  \def\werner@arg{#1}%
  \ifx\werner@arg\werner@stop
    \expandafter\env@ignore   % to end the recursion
  \else
    ``Here is #1\unskip''\par % what to do with #1
    \expandafter\item         % to continue the recursion
  \fi}

\edef\werner@stop{\noexpand\env@ignore\space}
\makeatother
\begin{document}

\begin{myitemize}
  \item First
  \item Second
  \item Last
\end{myitemize}

\end{document}

Note that \end{myitemize} should go on a line of its own (one could also take care of it appearing on the same line as the last item). The \unskip is necessary if \par is not next to #1 or isn't used at all, because of the end of line at the end of the item text.

enter image description here

A much more powerful implementation with expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentEnvironment{myitemize}{ O{} +b }
 {
  % do the setup
  \keys_set:nn { werner/itemize } { #1 }
  % split the contents at \item
  \seq_set_split:Nnn \l_werner_itemize_input_seq { \item } { #2 }
  % remove the first (empty) item
  \seq_pop_left:NN \l_werner_itemize_input_seq \l_tmpa_tl
  % issue the preamble
  \tl_use:N \l_werner_itemize_pre_tl
  % adorn the items
  \seq_set_map:NNn
    \l_werner_itemize_output_seq
    \l_werner_itemize_input_seq
    { \exp_not:n { \__werner_itemize_do:n { ##1 } } }
  % output the items, separated by the chosen separator
  \seq_use:NV \l_werner_itemize_output_seq \l_werner_itemize_sep_tl
  % issue the postamble
  \tl_use:N \l_werner_itemize_post_tl
 }
 {}

\seq_new:N \l_werner_itemize_input_seq
\seq_new:N \l_werner_itemize_output_seq
\cs_generate_variant:Nn \seq_use:Nn { NV }

\keys_define:nn { werner/itemize }
 {
  pre    .tl_set:N  = \l_werner_itemize_pre_tl,
  post   .tl_set:N  = \l_werner_itemize_post_tl,
  sep    .tl_set:N  = \l_werner_itemize_sep_tl,
  action .code:n    = \cs_set_eq:NN \__werner_itemize_do:n #1,
  action .initial:n = \use:n,
 }
\ExplSyntaxOff

\NewDocumentCommand{\quotepar}{m}{%
  ``#1''\par
}
\NewDocumentCommand{\normalitem}{m}{%
  \item #1
}
\NewDocumentCommand{\bulletitem}{m}{%
  \textbullet\ #1%
}

\begin{document}

\begin{myitemize}[action=\bulletitem,sep={, }]
  \item First
  \item Second
  \item Last
\end{myitemize}

\begin{myitemize}[action=\quotepar]
  \item First
  \item Second
  \item Last
\end{myitemize}

\begin{myitemize}[
  action=\normalitem,
  pre=\begin{itemize},
  post=\end{itemize},
]
  \item First
  \item Second
  \item Last
\end{myitemize}

\end{document}

enter image description here

  • How can this answer be updated to set myitemize just like an original itemize? If I just wrap the environment definition inside an itemize and immediately save \let\olditem\item before it's redefined, then \olditem ``...'' produces an extra item at the bottom of the list. – Werner Jan 21 '16 at 17:02
  • @Werner \newenvironment{myitemize}{\itemize}{\enditemize} – egreg Jan 21 '16 at 17:11
  • @Werner I added a new implementation – egreg Jan 21 '16 at 18:51

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.