[Tex/LaTex] Behaviour of enumitem \setlist

enumitemerrorslists

I have a package which creates some lists using enumitem. The standard way to change list parameters globally is to use \setlist, but this sometimes produces errors if the list is a custom one. Here's a minimal document.

In the code below, assume that the custom list test is defined in the package, i.e., the end user of the package doesn't see the \newlist and \setlist commands. They would assume, therefore that they could use \setlist[test]{nosep} to globally set the list spacing the test list. But this command fails with an error.

\documentclass{article}
\usepackage{enumitem}
% assume this code is in a package
\newlist{test}{enumerate}{1}
\setlist[test]{resume,leftmargin=*,label=(\arabic*)}
% end package code
% Now a user wants to adjust the list properties
\setlist[test]{nosep} % This doesn't work \setlist* is needed
%\setlist*[test]{nosep] % works and makes sense because it adds to list
%\setlist[test,1]{nosep} % But why does this work?

\begin{document}
\begin{test}
\item This is an item
\end{test}
\end{document}

There are two ways around this problem: 1) use \setlist* or 2) use \setlist[test,1], i.e. add an explicit level. Both options are not what an average user would expect to do.

Questions

  • Why does \setlist[test] fail and \setlist[test,1] work?
  • Is there a way around this, so that I don't need to instruct users how to set list properties explicitly?

Best Answer

Why does \setlist[test] fail and \setlist[test,1] work?

At its heart \setlist[<arg>] saves the settings passed to it to \enit@@<slightly modified version of 'arg'> with a \def.

Hence \setlist[test] overwrites an earlier \setlist[test], since they both save to the same macro \enit@@test. But \setlist[test,1] does not overwrite what \setlist[test] did because it saves to \enit@@testi.

Your example then fails because \setlist[test]{nosep} removes the earlier definition and in particular the label definition leaving your new test environment without a label.

I didn't realise it before and I wasn't even aware of \setlist*, but now it seems to me that \setlist* is more natural and should be the go-to command for changing list settings. But I doubt a lot of people feel the same or even know about \setlist*, so that might not be an option.

Hang on, why does an innocent \setlist[enumerate]{nosep} not cause these errors?

enumerate is special because it is defined in the LaTeX kernel. Its label definitions are normally set in the document class, so you can't run into trouble with \setlist[enumerate] overwriting enumitem settings. Your test on the other hand is a clean sheet for LaTeX and so it needs a label definition.

Aha! Why don't we just define a default counter as well?

Indeed

\newcounter{testi}
\renewcommand\thetesti{\arabic{testi}}
\def\labeltesti{(\thetesti)}
\newlist{test}{enumerate}{1}
\setlist[test]{resume,leftmargin=*,label=(\arabic*)}

would make the error go away if \setlist[test]{nosep} is used later on. But on closer inspection we find that that would only save \setlist[test]{label=(\arabic*)}. We would also have to set up resume,leftmargin=* ourselves. But those two can't be set up as easily as label=(\arabic*). You would end up giving away the clean interface of enumitem in your package.

Is there a way around this?

My conclusion from these investigations is that I would try to educate users to use \setlist* instead of \setlist. Sorry.

It is possible to make a definition like \setlist[test]{label=(\arabic*)} \setlist proof, but more complex beasts like \setlist[test]{resume,leftmargin=*,label=(\arabic*)} would require a lot of extra legwork that is probably not worth it in the end (if it is possible at all, I did not check if I could get something like resume off the ground).