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.

5
  • Related, but not answered explicitly: Is it possible to scan until the end of the current \item
    – Werner
    Aug 6, 2013 at 18:12
  • You have this in enumitem package, with itemize*.
    – Manuel
    Jan 21, 2016 at 19:05
  • @Manuel: I'm referring to the general use-case of capturing \items to do something with them.
    – Werner
    Jan 21, 2016 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, 2016 at 19:27
  • 1
    \eoldef in OpTeX. For example \eoldef\macro#1{parameter: #1} defines \macro with one parameter separated by end of current line.
    – wipet
    Jan 4, 2021 at 17:52

1 Answer 1

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

8
  • 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, 2016 at 17:02
  • @Werner \newenvironment{myitemize}{\itemize}{\enditemize}
    – egreg
    Jan 21, 2016 at 17:11
  • @Werner I added a new implementation
    – egreg
    Jan 21, 2016 at 18:51
  • @egreg Question, action .code:n can be written as action .cs_set:Np? Jan 5, 2021 at 6: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 5, 2021 at 8:46

You must log in to answer this question.

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