Matching and replacing newline character using Lua pattern patch

Viewed 119

This is a follow up to a question I asked in the LaTeX community regarding how to format items in the itemize environment. It turns out I got a response to that question using lua, but now I want to extend the lua code so I have a more lua programming centered question.

The answer proposes using string.gsub to replace pattern-matched parts of the string to something else. For example, the below code:

s = string.gsub ( s , '\\sitem%s+(.+)' , '\\item\\makefirstuc{%1},' )

will replace \item hello world to \item\makefirstuc{hello world}.

Here's the problem though, sometimes I have new lines in the string after item, for instance:

\item hello
world

I would like to replace that with:

\item\makefirstuc hello world

Does anyone know how I can do that?

Edit

I just tried the solution proposed by Wiktor but it wouldn't work for the case:

\item hello
world
\end{itemize}

Here's a full script to demonstrate:

-- test.lua
s = "\\sitem  Hello\n\\end{itemize}"

print(s)

result = string.gsub ( s, '\\item%s+(.+)' , function(x) return 
    '\\item\\makefirstuc{' .. string.gsub(x, '\n', ' ') .. '},' 
end )
print("\nAfter gsub")
print(result)

The above script outputs

\sitem  Hello
\end{itemize}

After gsub
\sitem  Hello
\end{itemize}

But I want it to output:

\sitem  Hello
\end{itemize}

After gsub
\item\makefirstuc {Hello},
\end{itemize}
3 Answers

No need for complicate lua constructs, you can simply use the getitems package:

\documentclass{article}

\usepackage{getitems}
\usepackage{mfirstuc}

% borrowed from biblatex
\makeatletter
\newcommand{\unspace}{%
  \ifbool{hmode}
    {\ifdimgreater\lastskip\z@
       {\unskip\unspace}
       {\ifnumgreater\lastpenalty\z@
          {\unpenalty\unspace}
          {}}}
    {}}  
\makeatother

\def\doitem#1{\item \makefirstuc{#1}\unspace\ifnum\thecurrentitemnumber=\thenumgathereditems.\else,\fi}%

\let\origitemize\itemize
\let\origenditemize\enditemize

\usepackage{environ}

\RenewEnviron{itemize}{%
  \expandafter\gatheritems\expandafter{\BODY}%
  \gathereditem{0}%
  \origitemize%
   \loopthroughitemswithcommand{\doitem}%
  \origenditemize%
}  

\begin{document}

\begin{itemize}
\item test
\item test
\item test
\end{itemize}

\end{document}

enter image description here

You can use a function as a replacement argument:

result = string.gsub ( s, '\\sitem%s+(.-)(\n\\)' , '\\item\\makefirstuc {%1},%2')

See the online demo.

Details:

  • \\sitem - a \sitem fixed string
  • %s+ - one or more whitespaces
  • (.-) - Group 1 (%1): any zero or more chars as few as possible
  • (\n\\) - Group 2 (%2): a newline and a \.

We don't want the . pattern match to be greedy, so this uses the frontier pattern to stop at the next backslash or at the end of the string. This will also mean that it can match multiple items, since the backslash isn't contained in the match. (see Lua Manual § 6.4.1 - Patterns)

%f[set], a frontier pattern; such item matches an empty string at any position such that the next character belongs to set and the previous character does not belong to set. The set set is interpreted as previously described. The beginning and the end of the subject are handled as if they were the character '\0'.

s = s:gsub(
    "\\sitem%s+(.-)\n?%f[\\\0]",
    function(it)
        return "\\item\\makefirstuc{" .. it:gsub("\n", " ") .. "},\n"
    end
)

Input:

\sitem Hello
World
\sitem Something else
\end{itemize}

Output:

\item\makefirstuc{Hello World},
\item\makefirstuc{Something else},
\end{itemize}
Related