This answer has grown a bit since I first wrote it.
I've come up with three solutions and didn't want to throw any of them away.
- The first (original) solution is simple because it doesn't add “and” to the penultimate item.
- The second solution does do this, but it is a little bit of a hack. I can't guarantee that it will continue to work if
enumitem
receives a big update.
- The third solution works pretty much like
enumitem
's inline lists. It has a huge limitation though: each items should be just a single paragraph and can't contain display equations.
1. Simple solution (no “and”)
Here's a possible solution that works by redefining \item
.
This may seem a little heavy-handed, but enumitems
's inline lists do this as well.
This won't add “and” to the penultimate item (see below for that).
I'm using SetEnumitemKey
to define a new key, sentence
, that can be supplied to any list environment that enumitem
knows about.
It will append a period (.) to the last item and a semicolon (;) to every other item. You can use the itemjoin
key after sentence
to change the semicolon to something else.
\documentclass{article}
\usepackage{enumitem}
\SetEnumitemKey{sentence}{%
before*=\sentencelistprep,
after*={\unskip.},
itemjoin={;},
}
\let\sentenceitemjoin\empty
\edef\sentenceitem{\noexpand\sentenceitemjoin\unexpanded\expandafter{\item}}%
\makeatletter %% <- make @ usable in command sequences
\newcommand*\sentencelistprep{%
\def\sentenceitemjoin{\def\sentenceitemjoin{\unskip\enit@itemjoin}}%
\let\item\sentenceitem
}
\makeatother %% <- revert @
\begin{document}
Here is an \texttt{itemize} environment whose items are part of this sentence and therefore come with appropriate punctuation:
\begin{itemize}[sentence]
\item a list item that could go first but also last
\item another list item that I'm not sure about that is long enough to span two lines
\item this one should go last unless I think of another one
\end{itemize}
It's convenient because I can add, remove, and rearrange items without worrying about punctuation.
\end{document}
Warning: you can't use any other list environments inside one with the sentence
key because the redefinition of \item
will also apply to those and will insert additional semicolons there as well.
(You probably wouldn't want to nest lists inside a running sentence, but if you have to you can add \let\sentenceitemjoin\empty
inside the inner list.)
Remarks
- I didn't include the colon (:) because does not feel like it is a part of the list (and not affected if items are reordered).
If you do want the colon to automatically be inserted you can replace the value of
before*
above by {\unskip:\sentencelistprep}
.
If you don't want to have to supply the sentence
key every time, you can create a custom list environment that includes it with e.g.
\newlist{myitemize}{itemize}{1}
\setlist[myitemize]{label=\textbullet,sentence}
The before
and after
keys overwrite sentence
if supplied later, but the converse is not true because sentence
uses the starred versions before*
and after*
.
\sentencelistprep
, which is called at the start of a list with sentence
, does the following: it defines \sentenceitemjoin
as\def\sentenceitemjoin{\unskip\enit@itemjoin}
and replaces \item
by a version that includes \sentenceitemjoin
.
The first \item
therefore redefines \sentenceitemjoin
to \unskip\enit@itemjoin
(the value of the itemjoin
key), and every \item
after that consequently inserts a ;
before it.
2. Somewhat hacky solution (includes “and”)
Per your request, here is a way to add “; and” to the penultimate item.
I couldn't do this by simply modifying the other solution because an \item
s in a list environment can't really know if it is the final one.
The following works by first scanning the contents of the entire list environment and inserting ;
s at the end of every item but the last two, and adding ; and
to the penultimate item. It's a little bit of a hack, but it works.
You can use the itemjoin
, itemjoin*
and itemjoin**
keys to change ;
, ; and
and .
to something else.
\documentclass{article}
\usepackage{enumitem}
\usepackage{environ} %% <- for \Collect@Body
\makeatletter %% <- make @ usable in command sequences
\long\def\sentencelist@afterfi#1\fi{ %% <- insert sentencelist code after \fi (hack!)
#1\fi\Collect@Body\sentencelist %% <- apply \sentencelist to the body of the environment
}
\long\def\sentencelist#1{\@sentencelist#1\item\@sentencelist}
\long\def\@sentencelist#1\item#2\@sentencelist{
#1\@@sentencelist#2\@@sentencelist %% <- run preamble as normal
}
\long\def\@@sentencelist#1\item#2\@@sentencelist{%
\if\relax\detokenize{#2}\relax %% <- if last item
\if@newlist\else\unskip\enit@itemjoin@s\fi %% <- ..insert "; and" if not also the first item
\item #1\unskip\enit@itemjoin@ss% %% <- ..insert the \item and a "."
\else %% <- otherwise
\if@newlist\else\unskip\enit@itemjoin\fi %% <- .. insert ";" if not also the first item
\item #1% %% <- ..insert the item and a ;
\@@sentencelist#2\@@sentencelist %% <- ..and repeat
\fi
}
\let\enit@itemjoin@ss\@empty
\enitkv@key{}{itemjoin**}{% %% <- create "itemjoin**" key
\def\enit@itemjoin@ss{#1}}
\SetEnumitemKey{sentence}{% %% <- create "sentence" key
before*=\sentencelist@afterfi,
itemjoin={;}, itemjoin*={; and}, itemjoin**={.}
}
\makeatother %% <- revert @
\begin{document}
Here is an \texttt{itemize} environment whose items are part of this sentence and therefore come with appropriate punctuation:
\begin{itemize}[sentence]
\item a list item that could go first but also last
\item another list item that I'm not sure about that is long enough to span two lines
\item this one should go last unless I think of another one
\end{itemize}
It's convenient because I can add, remove, and rearrange items without worrying about punctuation.
\end{document}
3. Solution that mimicks enumitem
's inline lists
Here is a method that very closely resembles how enumitem
's inline lists work.
It's quite ingenious and I don't think I would have been able to come up with it myself.
You can customise it with the itemjoin
, itemjoin*
and itemjoin**
keys.
A big limitation is that the item's in this list can only be single paragraphs: they can't contain paragraph breaks, display equations, other lists, etc.
I don't think there's not much that can be done about this (without switching to a different method) and I suspect this is the reason why enumitem
's itemjoin
key has not been implemented for normal lists.
\documentclass{article}
\usepackage{enumitem,amsmath}
\begin{document}
\makeatletter
\newif\ifsentence@firstitem
\newcommand*\sentencebefore{%
\let\sentence@olditem\item
\let\item\sentence@item
\sentence@firstitemtrue
}
\newcommand*\sentenceafter{%
\egroup
\ifhmode\unskip\enit@itemjoin@s\fi
\sentence@olditem\unhbox\enit@inbox\unskip\enit@itemjoin@ss
}
\def\sentence@item{%
\ifhmode
\egroup
\ifsentence@firstitem
\sentence@firstitemfalse
\else
\unskip\enit@itemjoin
\fi
\sentence@olditem \unhbox\enit@inbox
\fi
\setbox\enit@inbox=\hbox\bgroup%\parshape1\@totalleftmargin\linewidth
}
\let\enit@itemjoin@ss\@empty
\enitkv@key{}{itemjoin**}{%
\def\enit@itemjoin@ss{#1}}
\makeatother
\SetEnumitemKey{sentence}{%
before*=\sentencebefore, after*=\sentenceafter,
itemjoin=;, itemjoin*={; and}, itemjoin**=.
}
Here is an \texttt{itemize} environment whose items are part of this sentence and therefore come with appropriate punctuation:
\begin{itemize}[sentence]
\item a list item that could go first but also last
\item another list item that I'm not sure about that is long enough to span two lines
\item this one should go last unless I think of another one
\end{itemize}
It's convenient because I can add, remove, and rearrange items without worrying about punctuation.
\end{document}
label={; and}
?before
before the list,itemjoin
to the end of every item in the list except the penultimate and last ones,itemjoin*
to the penultimate item, andafter
to the last item. Usinglabel={; and}
changes the label of every item to be; and
, which is definitely not the effect I'm going for.