17

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.

Werner
  • 603,163

1 Answers1

13

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 { __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

egreg
  • 1,121,712
  • 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
  • @egreg Question, action .code:n can be written as action .cs_set:Np? – Pablo González L Jan 05 '21 at 06:45
  • @PabloGonzálezL They are very different: .code:n executes the code at key evaluation, whereas .cs_set:Np will store the code as the replacement text for a function/macro. – egreg Jan 05 '21 at 08:46
  • @egreg Thank you very much for the clarification, just one last question, any reason why you haven't declared the _tl before using them?...I always learn from your answers and you usually do. – Pablo González L Jan 05 '21 at 15:12
  • @PabloGonzálezL Variables appearing in \keys_define:nn are automatically allocated, if not already done. Forcing to declare them beforehand is thought as unnecessary code duplication. – egreg Jan 05 '21 at 15:17
  • @egreg Right!!!, I have a lot to learn :) – Pablo González L Jan 05 '21 at 15:22