# Simpler notation for enumerated lists

I'm creating documents with many multilevel enumerated lists and I'm looking for ways to simplyfy the notation.

Ideally I would like to write something like the following:

\documentclass{article}
\usepackage{enumitem}

\begin{document}

Some text here
\i first item
\i second item

more text

\i third item
\par third item continues

\i fourth item

\ii fifth item

\ii sixth item
\ii seventh item

more text

\ii eighth item
\i ninth item

\end{document}


And I would like it to be translated to:

\documentclass{article}
\usepackage{enumitem}

\begin{document}

Some text here
\begin{enumerate}[resume=level1]
\item\setcounter{enumii}{0} first item
\item\setcounter{enumii}{0} second item
\end{enumerate}
more text
\begin{enumerate}[resume=level1]
\item\setcounter{enumii}{0} third item
\par third item continues
\item\setcounter{enumii}{0} fourth item
\begin{enumerate}[resume=level2]
\item fifth item

\item sixth item
\item seventh item
\end{enumerate}
\end{enumerate}
more text
\begin{enumerate}[resume=level1]
\item[]
\begin{enumerate}[resume=level2]
\item eighth item
\end{enumerate}
\item\setcounter{enumii}{0} ninth item
\end{enumerate}

\end{document}


More specifically:

• If \i is not preceeded by another item, then \begin{enumerate}[resume=level1] is added before.
• If \i is not followed by a paragraph not beginning with \i or \ii, then \end{enumerate} is added after.
• If paragraph break is marked with \par instead of two line braks, then it is included in the previous item and the list is not terminated.
• Level2 items \ii are preceeded or followed by \begin{enumerate}[resume=level2] and \end{enumerate} accordingly.
• If \ii is not preceeded by level 1 item or level 2 item, then \begin{enumerate}[resume=level1] and a dummy level 1 item \item[] are also added.
• If \ii should resume from the previous level 2 numbering if there has been no level 1 item inbetween.

Would it be possible with reasonable effor to define macros that would achieve this?

• What's the purpose of the five \setcounter{enumii}{0} instructions? – Mico Feb 23 at 9:32
• \i is already defined, you shouldn't redefine it. – Ulrike Fischer Feb 23 at 14:05

Here's a LuaLaTeX-based solution. Its three main working assumptions are (A) there are two levels of enumerated lists, (B) the strings \i and \ii occur at the start of a line, while possibly being preceded by whitespace, and (C) any lines with \par instructions are not preceded by all-blank lines. The third assumption is not (at least not explicitly) in the the OP's list of working assumptions; however, without this assumption the already-complicated code even more complicated.

The bulk of the work is performed by a Lua function called FancyEnum. Two utility LaTeX macros, called \FancyEnumOn and \FancyEnumOff, serve to activate and deactivate the Lua function's operation on the input stream. For sure, you should run \FancyEnumOff ahead of verbatim material that features the strings \i and \ii.

Update, after receiving more information from the OP about the purpose of the \setcounter{enumii}{0} directives. (Their purpose was to make sure that a "new" level-2 list -- where "new" means that there's been at least one intervening level-1 \item directive -- starts at 0.) The purpose is much more easily achieved by running \begin{enumerate}[series=level2] instead of \begin{enumerate}[resume=level2] whenever a new level-2 lists begins. I've updated the Lua code accordingly.

The following screenshot shows the result of the operations of the Lua function on (a slightly modified form of) the OP's suggested example on the left and the corresponding hard-coded enumerated lists on the right.

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{enumitem}
\usepackage{multicol} % to create two-column output

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

-- Begin by defining 2 Boolean variables. They will be set to 'true' if
-- LaTeX is in a level-1 or level-2 enumerated environment, respectively.
local in_enumi =false
local in_enumii=false

-- The Lua function 'FancyEnum' does most of the work.
function FancyEnum ( s )
-- Input line starts with '\i' (possibly preceded by whitespace):
if s:find ( "^%s-\\i " ) then
if in_enumii==true then -- need to fall back one list level
s = s:gsub ( "^%s-\\i " , "\\end{enumerate}\\item " )
in_enumii=false
elseif in_enumi==true then -- continue at level 1
s = s:gsub ( "^%s-\\i " , "\\item " )
else -- neither in_enumi nor in_enumii are true -- start a level-1 list
s = s:gsub ( "^%s-\\i " , "\\begin{enumerate}[resume=level1] \\item " )
in_enumi=true
end
-- Input line starts with '\ii' (possibly preceded by whitespace):
elseif s:find ( "^%s-\\ii " ) then
if in_enumii==true then -- continue at level 2
s = s:gsub ( "^%s-\\ii " , "\\item " )
elseif in_enumi==true then -- Moving from level 1 to level 2. Hence,
-- we use '[series=level2]' rather than '[resume=level2]'
in_enumii=true
s = s:gsub ( "^%s-\\ii " , "\\begin{enumerate}[series=level2]\\item " )
else -- jumping straight to level-2 list
in_enumi=true
in_enumii=true
s = s:gsub ( "^%s-\\ii " , "\\begin{enumerate}[resume=level1]\\item[]" ..
"\\begin{enumerate}[resume=level2]\\item " )
end
-- Input line is all-blank. Terminate 'enumerate' if 'in_enumi' and/or
-- 'in_enumii' are 'true'.
elseif s=="" then
if in_enumii==true then    -- terminate two 'enumerate' levels
s = "\\end{enumerate}\\end{enumerate}"
in_enumii=false
in_enumi=false
elseif in_enumi==true then -- terminate one 'enumerate' level
s = "\\end{enumerate}"
in_enumi=false
end
end

-- Place 's' back on the input stream, for further processing by LaTeX.
return s
end -- end of Lua function

\end{luacode*}

%% Two LaTeX utility macros that activate and deactivate 'FancyEnum':
"process_input_buffer", FancyEnum, "FancyEnum")}}
\newcommand\FancyEnumOff{\directlua{luatexbase.remove_from_callback(
"process_input_buffer", "FancyEnum")}}

\begin{document}
\begin{multicols}{2}

\FancyEnumOn % activate the Lua function 'FancyEnum'
\verb+\FancyEnum+ on

\i first item
\i second item

more text

\i third item
\par third item continues % note: no blank line before this line

\i fourth item

\ii fifth item

\ii sixth item
\par sixth item continues  % note: no blank line before this line

\i  seventh item
\ii eighth item  % note: no blank line before this line

more text

\ii ninth item

\i tenth item

\FancyEnumOff % deactivate 'FancyEnum'
\columnbreak % optional

\verb+\FancyEnum+ off (explicit lists)
\begin{enumerate}[series=level1] % not [resume=level1]
\item first item
\item second item
\end{enumerate}
more text
\begin{enumerate}[resume=level1]
\item third item

third item continues
\item fourth item

\begin{enumerate}[series=level2]
\item fifth item

\item sixth item

sixth item continues
\end{enumerate}
\item seventh item
\begin{enumerate}[series=level2]
\item eighth item
\end{enumerate}
\end{enumerate}
more text
\begin{enumerate}[resume=level1]
\item[]
\begin{enumerate}[resume=level2]
\item ninth item
\end{enumerate}
\item tenth item
\end{enumerate}

\end{multicols}
\end{document}

• The purpose of \setcounter{enumii}{0} directives is to make sure that level 2 enumeration always starts at 0 if there is a level 1 item in between. In your screenshot the second level enumeration after item 9 should start at (a). I ckecked and this is what happens if I compile my example code. Of course there might be a better way to achieve that result. – ttsc Feb 23 at 15:41
• @ttsc - Thanks for this explanation. I don't think that running \setcounter{enumii}{0} actually helps achieve your objective. (Moreover, the sample code you provided in your query doesn't actually give an example that would demonstrate this objective in action.) Let me think a bit about how your objective might be achieved in the setting used in my answer. – Mico Feb 23 at 15:53
• @ttsc - Please see if the updated code works for you. (Switch from luacode to luacode* environment.) – Mico Feb 23 at 19:52
• @ttsc - Rather than run \setcounter{enumii}{0} each and every time a level-1 \item directive is encountered, just run \begin{enumerate}[series=level2] instead of \begin{enumerate}[resume=level2] when switching from a level-1 to a level-2 list. I'm updating the Lua code and the example to reflect this change. – Mico Feb 23 at 20:37
• @ttsc - You asked in an earlier comment, "I wonder if [LuaLaTeX] is completely compatible with the old way of writing LaTeX? LuaLaTeX is largely a superset of pdfLaTeX, with two very significant additions: The availability of the scripting language Lua and the ability to use system fonts (in practice mainly via the fontspec). Nowadays, LuaLaTeX is highly stable and almost entirely backward compatible with pdfLaTeX. The main adjustment you may have to make is to stop loading the inputenc and fontenc package; in their place, load the fontspec package. – Mico Feb 23 at 21:50

I think that you want to use the easylist package. It does not quite give you all of the features that you want but it is pretty close. For example, the code:

\documentclass{article}
\usepackage[sharp]{easylist}% sharp => # is used for \item
\begin{document}

Some text here
\begin{easylist}[articletoc]
# first item
# second item

more text

# third item
\par third item continues

# fourth item

## fifth item

## sixth item
## seventh item

more text

## eighth item
# ninth item
\end{easylist}

\end{document}


produces

There are many extra options that can be controlled using \ListProperties, such as Hang=<n> for setting hanging indents. The manual is very readable.

EDIT The folllowing might be close enough to what you want. You can disable the counter for the first list level using

\ListProperties(
Hang=true,
Hide1=1,
Hide2=1,
Hide3=1,
Hide4=1,
Progressive*=2em,
Style1*=\theblank,
Indent1=0em,
)


As a result, the following code

\begin{easylist}
# Some text here
## first item
## second item

# more text

## third item
\par third item continues

## fourth item

### fifth item

### sixth item
### seventh item

# more text

### eighth item
## ninth item
\end{easylist}


produces

So every line has at least one #, corresponding to not being inside a list, whereas the lines with ## are inside the first list (level 1), those with ### are at level 2 and so on.

Here is the full code:

\documentclass{article}
\usepackage[sharp]{easylist}% sharp => # is used for \item
\begin{document}

\ListProperties(
Hang=true,
Hide1=1, % hide counter 1 on level 1
Hide2=1, % hide counter 1 on level 2
Hide3=1, % hide counter 1 on level 3
Hide4=1, % hide counter 1 on level 4
Progressive*=2em,
Style1*=\theblank,
Indent1=0em,
)

\begin{easylist}
# Some text here
## first item
## second item

# more text

## third item
\par third item continues

## fourth item

### fifth item

### sixth item
### seventh item

# more text

### eighth item
## ninth item
\end{easylist}

\end{document}

• What are the features that are not provided by easylist? – ttsc Feb 24 at 10:01
• @ttsc I may have missed it in the manual, but I don't think that there's an easy way to have the same list level before and after a sublist. – Andrew Feb 24 at 10:06
• I'm not sure what you mean. Could you give an example? – ttsc Feb 24 at 10:08
• For my purpose it is important to have normal paragraph text with sections an tables between items and the normal text should not be formatted as a continuation of the list item but as a normal paragraph. Also, I also need to have pictures and tables and multiple paragraphs inside list items. Are these features available in easylist? – ttsc Feb 24 at 10:14
• @ttsc For example, your "more text" lines are supposed to be at "level 0", which is outside of a list/enumerate environment, but I don't know how to break out of level 1 (i.e. inside a list) and return temporarily to level 0. To return to level 0 you can, of course, close the easylist environment but if you have a list item at level 1 that contains a sublist at level 2 the only way to return to level 1 is to start a new level one \item, which may not be what you want. – Andrew Feb 24 at 10:14