6

I declared a new command to format list elements in LaTeX as follows:

\newcommand{\sitem}[1]{\item \makefirstuc{#1},}
\newcommand{\eitem}[1]{\item \makefirstuc{#1}.}

This works OK, but now I have to brace every list element as follows:

\begin{itemize}
    \sitem{First item}
    \eitem{second item}
\end{itemize}

How do I get it so that the following is possible?

\begin{itemize}
    \sitem First item
    \eitem second item
\end{itemize}
Mico
  • 506,678
user32882
  • 1,594

3 Answers3

5

Here's a LuaLaTeX-based solution. It sets up a Lua function, called process_items, which acts as a preprocessor: By the time TeX gets to process the input lines in question, it will not "see" \sitem first item but, instead, \item First item,. (Note the uppercasing of the first letter and the terminal comma.) The Lua function is activated and deactivated, respectively, by the LaTeX utility macros \ItemOn and \ItemOff.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article} % or some other suitable document class
\usepackage{mfirstuc}   % for '\makefirstuc' macro

\usepackage{luacode} % for 'luacode' environment \begin{luacode}

function process_items ( s )
  s = string.gsub ( s , '\\sitem%s+(.+)' , '\\item\\makefirstuc{%1},' )
  s = string.gsub ( s , '\\eitem%s+(.+)' , '\\item\\makefirstuc{%1}.' )
  return s
end
\end{luacode}

\newcommand\ItemOn{\directlua{luatexbase.add_to_callback ( 
  "process_input_buffer", process_items , "ProcessItems" )}}
\newcommand\ItemOff{\directlua{luatexbase.remove_from_callback ( 
  "process_input_buffer", "ProcessItems" )}}

\begin{document}
\ItemOn %% assign the Lua function to the input processor callback

\begin{itemize}
    \sitem first item
    \sitem  second item
    \eitem   third item 
\end{itemize}
\end{document}
Mico
  • 506,678
  • 1
    It works. I don't fully understand how but the more I use it the more it'll sink in I guess. Why are you not using ItemOff in this example? – user32882 Apr 19 '22 at 13:19
  • 1
    @user32882 - I guess I created \ItemOff mainly as counterpart to the much more important \ItemOn macro. The code works because the Lua function is assigned to LuaTeX's process_input_buffer callback, meaning that it acts as a prepocessor and modifies matching patterns. By the time LaTeX gets to do its job, it never gets to "see" \sitem and \eitem. While \sitem and \eitem look like they might be LaTeX macros, that's not the case. Instead, their role is to serve as "markers" for pattern matching by Lua's powerful built-in string.gsub ("generalized substitution") function. – Mico Apr 19 '22 at 14:09
  • 1
    I'm afraid this breaks for multiple lines after the \sitem and \eitem. Any good ideas on how I can remedy that? – user32882 Apr 19 '22 at 14:41
  • 2
    @user32882 --- You asked for something that takes the rest of your line as an argument. If you want to allow multiple lines then TeX could only detect the end of the argument at the next instance of \sitem/\eitem or the \end{itemize}. Then you would have all sorts of fun and games with spaces between the argument and the full stop or comma. I suspect this is more trouble than it's worth. – Ian Thompson Apr 19 '22 at 15:02
  • @user32882 - In the title of your query, you specified "takes the rest of [the] line as [its] argument". That's exactly what my answer delivers. If that's not what you actually wanted, please clarify the condition(s) the input is supposed to satisfy. – Mico Apr 19 '22 at 15:05
  • @IanThompson this is worthwhile for me because I use itemize a lot – user32882 Apr 19 '22 at 15:05
  • 1
    @Mico I understand. I made another question: https://tex.stackexchange.com/questions/641312/lua-approach-for-itemize-fails-if-multiple-lines-are-given-after-item – user32882 Apr 19 '22 at 15:06
4

You can borrow the \eoldef macro from OpTeX. Syntax:

\eoldef\macro #1{body of the macro, #1 can be used}

The \macro is defined with its parameter separated by the end of line.

In your LaTeX document it can look like this:

\documentclass{article}

\def\eoldef #1{\def #1{\begingroup \catcode\^^M=12 \eoldefA #1}% \expandafter\def\csname \string #1:M\endcsname} {\catcode^^M=12 % \gdef\eoldefA #1#2^^M{\endgroup\csname \string #1:M\endcsname{#2}}% }

\def\firstupper#1{\uppercase{#1}}

\eoldef\sitem#1{\item \firstupper #1,} \eoldef\eitem#1{\item \firstupper #1.}

\begin{document}

\begin{itemize} \sitem First item \eitem second item \end{itemize}

\end{document}

wipet
  • 74,238
4

If you don't plan to use nested lists in this case, you can implement the job in expl3.

Split the input at \item and massage the resulting sequence so that uppercasing of the first letter is performed. Then the whole lot is delivered adding ,\item in between items and a final period.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentEnvironment{adjitemize}{+b} { \adjitemize_main:n { #1 } } {}

\seq_new:N \l__adjitemize_in_seq \seq_new:N \l__adjitemize_out_seq

\cs_new_protected:Nn \adjitemize_main:n { % split the body at \item \seq_set_split:Nnn \l__adjitemize_in_seq { \item } { #1 } % discard the first item, which is empty \seq_pop_left:NN \l__adjitemize_in_seq \l_tmpa_seq % add the capitalization of the first letter \seq_set_map_x:NNn \l__adjitemize_out_seq \l__adjitemize_in_seq { \text_titlecase:n { ##1 } } % deliver the result \begin{itemize} \item \seq_use:Nn \l__adjitemize_out_seq { , \item } . \end{itemize} }

\ExplSyntaxOff

\begin{document}

\begin{adjitemize} \item first item with a second line \item second item \item final item \end{adjitemize}

\end{document}

I believe that using \item is much simpler than \sitem and \eitem, because you can move around the items without worrying about which one is last. The only thing to remember is to use adjitemize instead of itemize, but this is justified by the fact that you want to massage the items.

enter image description here

egreg
  • 1,121,712
  • This is a valid answer to my other question. If you copy/paste it there I will accept it. – user32882 Apr 20 '22 at 06:42
  • @user32882 you should remove the other question, instead – egreg Apr 20 '22 at 07:47
  • Sure, I can delete it. But I'm keeping @Mico's answer for this question here. Your answer is also technically valid, and good reference for the future, but I still need to brush up my expl3 skills :) – user32882 Apr 20 '22 at 07:53