ltlists.dtx

Johannes Braams  David Carlisle  Alan Jeffrey
Leslie Lamport  Frank Mittelbach  Tobias Oetiker^{∗}
Chris Rowley  Rainer Schöpf

1995/11/07

1 List, and related environments

The generic commands for creating an indented environment – enumerate,

itemize, quote, etc – are:

\list{⟨LABEL⟩}{⟨COMMANDS⟩} ... \endlist

which can be invoked by the user as the list environment. The LABEL argument specifies item labeling. COMMANDS contains commands for changing the horizontal and vertical spacing parameters.

Each item of the environment is begun by the command \item[ITEMLABEL] which produces an item labeled by ITEMLABEL. If the argument is missing, then the LABEL argument of the \list command is used as the item label.

The label is formed by putting \makelabel{⟨ITEMLABEL⟩} in an hbox whose width is either its natural width or else \labelwidth, whichever is larger. The \list command defines \makelabel to have the default definition:

\makelabel{⟨ARG⟩} == BEGIN \hfil ARG END

which, for a label of width less than \labelwidth, puts the label flushright, \labelsep to the left of the item’s text. However, \makelabel can be \let to another command by the \list’s COMMANDS argument.

A \usecounter{⟨foo⟩} command in the second argument causes the counter foo to be initialized to zero, and stepped by every \item command without an argument. (\label commands within the list refer to this counter.)

When you leave a list environment, returning either to an enclosing list or normal text mode, LaTeX begins a new paragraph if and only if you leave a blank line after the \end command. This is accomplished by the \@endparenv command.

Blank lines are ignored every other reasonable place–i.e.:

• Between the \begin{list} and the first \item, • Between the \item and the text of that item.

∗Tobi has converted the documentation to doc.sty standard

• Between the end of the last item and the \end{list}.

For an environment like quotation, in which items are not labeled, the entire environment is a single item. It is defined by letting \quotation == \list{}{...}\item\relax. (Note the \relax, there in case the first character in the environment is a ’[’.) The spacing parameters provide a great deal of flexability in designing the format, including the ability to let the indentation of the first paragraph be different from that of the subsequent ones.

The trivlist environment is equivalent to a list environment whose second argument sets the following parameter values:

\leftmargin = 0 : causes no indentation of left margin

\labelwidth = 0 : see below for precise effect this has.

\itemindent = 0 : with a null label, makes first paragraph have no indentation. Succeeding paragraphs have \parindent indentation. To give first paragraph same indentation, set \itemindent = \parindent before the

     \item[].

Every \item in a trivlist environment must have an argument—in many cases, this will be the null argument (\item[]). The trivlist environment is mainly used for paragraphing environments, like verbatim, in which there is no margin change. It provides the same vertical spacing as the list environment, and works reasonably well when it occurs immediately after an \item command in an enclosing list.

1.1 List and Trivlist

The following variables are used inside a list environment:

\@totalleftmargin The distance that the prevailing left margin is indented from the outermost left margin,

\linewidth The width of the current line. Must be initialized to \hsize.

\@listdepth A count for holding current list nesting depth.

\makelabel A macro with a single argument, used to generate the label from the argument (given or implied) of the \item command. Initialized to \@mklab by the \list command. This command must produce some stretch—i.e.,

     an \hfil.

\@inlabel A switch that is false except between the time an \item is encountered and the time that TEX actually enters horizontal mode. Should be tested by commands that can be messed up by the list environment’s use of \everypar.

\boxlabels When @inlabel = true, it holds the labels to be put out by

     \everypar.

@noparitem A switch set by \list when @inlabel = true. Handles the case of a \list being the first thing in an item.

@noparlist A switch set true for a list that begins an item. No \topsep space is added before or after \item’s such a list.

@newlist Set true by \list, set false by the first text (by \everypar).

@noitemarg Set true when executing an \item with no explicit argument. Used to save space. To save time, make two separate \@item commands.

@nmbrlist Set true by \usecounter command, causes list to be numbered.

\@listctr \def’ed by \usecounter to name of counter.

\@noskipsec A switch set true by a sectioning command when it is creating an in-text heading with \everypar.

Throughout a list environment, \hsize is the width of the current line, measured from the outermost left margin to the outermost right margin. Environments like tabbing should use \linewidth instead of \hsize.

Here are the parameters of a list that can be set by commands in the \list’s COMMANDS argument. These parameters are all TeX skips or dimensions (defined by \newskip or \newdimen), so the usual TEX or L^{A}TEX commands can be used to set them. The commands will be executed in vmode if and only if the \list was preceded by a \par (or something like an \end{list}), so the spacing parameters can be set according to whether the list is inside a paragraph or is its own paragraph.

1.2 Vertical Spacing (skips)

\topsep: Space between first item and preceding paragraph.

\partopsep: Extra space added to \topsep when environment starts a new paragraph (is called in vmode).

\itemsep: Space between successive items.

\parsep: Space between paragraphs within an item – the \parskip for this environment.

1.3 Penalties

\@beginparpenalty: put at the beginning of a list

\@endparpenalty: put at end of list

\@itempenalty: put between items.

1.4 Horizontal Spacing (dimens)

\leftmargin: space between left margin of enclosing environment (or of page if top level list) and left margin of this list. Must be nonnegative.

\rightmargin: analogous.

\listparindent: extra indentation at beginning of every paragraph of a list except the one started by the \item command. May be negative! Usually, labeled lists have \listparindent equal to zero.

\itemindent: extra indentation added right BEFORE an item label.

\labelwidth: nominal width of box that contains the label. If the natural width of the label <= \labelwidth, then the label is flushed right inside a box of width \labelwidth (with an \hfil). Otherwise, a box of the natural width is employed, which causes an indentation of the text on that line.

\labelsep: space between end of label box and text of first item.

1.5 Default Values

Defaults for the list environment are set as follows. First, \rightmargin, \listparindent and \itemindent are set to 0pt. Then, one of the commands \@listi, \@listii, ... , \@listvi is called, depending upon the current level of the list. The \@list … commands should be defined by the document style. A convention that the document style should follow is to set \leftmargin to \leftmargini,…, \leftmarginvi for the appropriate level. Items that aren’t changed may be left alone, but everything that could possibly be changed must be reset.

\list{LABEL}{COMMANDS} == BEGIN

     if \@listdepth > 5

then LaTeX error: ’Too deeply nested’

       else \@listdepth :=G \@listdepth + 1

fi

     \rightmargin  := 0pt
     \listparindent := 0pt
     \itemindent  := 0pt
     \eval(@list \romannumeral\the\@listdepth)  %% Set default values:
     \@itemlabel  :=L LABEL
     \makelabel  == \@mklab

@nmbrlist :=L false

COMMANDS

\@trivlist % commands common to \list and

\trivlist

\parskip  :=L \parsep
\parindent  :=L \listparindent
\linewidth  :=L \linewidth - \rightmargin -\leftmargin
\@totalleftmargin :=L \@totalleftmargin + \leftmargin
\parshape 1 \@totalleftmargin \linewidth
\ignorespaces                    % gobble space up to \item

END

\endlist == BEGIN \@listdepth :=G \@listdepth -1
              \endtrivlist

END

\@trivlist ==

BEGIN

if @newlist = T then \@noitemerr fi

%% This command removed for some forgotten

reason.

\@topsepadd :=L \topsep

if @noskipsec then leave vertical mode fi %% Added 11 Jun 85 if vertical mode

  then \@topsepadd :=L \@topsepadd + \partopsep

else \unskip \par % remove glue from end of last line fi if @inlabel = true

then @noparitem :=L true

@noparlist :=L true else @noparlist :=L false

        \@topsep :=L \@topsepadd

fi

\@topsep  :=L \@topsep + \parskip  %% Change 4 Sep 85

\leftskip  :=L 0pt  % Restore paragraphing

parameters

\rightskip  :=L \@rightskip
\parfillskip  :=L 0pt + 1fil

NOTE: \@setpar called on every \list in case \par has been temporarily munged before the \list command.

\@setpar{if @newlist = false then {\@@par} fi}

\@newlist  :=G T
\@outerparskip  :=L \parskip

END

\trivlist ==

BEGIN

\parsep  := \parskip

@nmbrlist := F

\@trivlist
\labelwidth := 0
\leftmargin := 0
\itemindent := \parindent

\@itemlabel :=L ”empty” %% added 93/12/13

\makelabel{LABEL} == LABEL

END

\endtrivlist ==

BEGIN if @inlabel = T then \indent fi if horizontal mode then \unskip \par fi if @noparlist = true

   else if \lastskip > 0
           then \@tempskipa := \lastskip
                \vskip - \lastskip
                \vskip \@tempskipa -\@outerparskip + \parskip

fi

        \@endparenv

fi END

\@endparenv ==

BEGIN \addpenalty{@endparpenalty}

\addvspace{\@topsepadd}
\endgroup    %% ends the \begin command’s \begingroup

\par == BEGIN

            \@restorepar
            \everypar{}
            \par

END

\everypar == BEGIN remove \lastbox \everypar{} END
\begingroup  %% to match the \end commands \endgroup

END

\item == BEGIN if math mode then WARNING fi if next char = [

              then \@item

else @noitemarg := true

\@item[@itemlabel]

END

\@item[LAB] ==

BEGIN if @noparitem = true then @noparitem := false

% NOTE: then clause hardly every taken,

    % so made a macro \@donoparitem
\box\@labels :=G \hbox{\hskip -\leftmargin
                       \box\@labels
                       \hskip \leftmargin }

if @minipage = false then

   \@tempskipa := \lastskip
   \vskip -\lastskip
   \vskip \@tempskipa + \@outerparskip - \parskip

fi else if @inlabel = true

then \indent \par % previous item empty. fi if hmode then 2 \unskip's

% To remove any space at end of prev. % paragraph that could cause a blank line.

         \par

fi if @newlist = T

then if @nobreak = T % Kludge if list follows \section

          then \addvspace{\@outerparskip - \parskip}
          else \addpenalty{\@beginparpenalty}
               \addvspace{\@topsep}
               \addvspace{-\parskip}   %% added 4 Sep 85

fi

   else \addpenalty{\@itempenalty}
        \addvspace{\itemsep}

fi @inlabel :=G true

fi

\everypar{ @minipage :=G F @newlist :=G F if @inlabel = true

then @inlabel :=G false

           \hskip -\parindent
           \box\@labels
           \penalty 0

%% 3 Oct 85 – allow line break here

           \box\@labels :=G null

fi

    \everypar{} }

@nobreak :=G false if @noitemarg = true

then @noitemarg := false if @nmbrlist

             then \refstepcounter{\@listctr}

fi fi

    \@tempboxa :=L \hbox{\makelabel{LAB}}
    \box\@labels :=G \@labels \hskip \itemindent
                      \hskip - (\labelwidth + \labelsep)
                      if \wd \@tempboxa > \labelwidth
                         then \box\@tempboxa
                         else \hbox to \labelwidth
{\unhbox\@tempboxa}

fi

                      \hskip\labelsep

\ignorespaces %gobble space up to text END

\makelabel{LABEL} == ERROR %% default to catch lonely \item

\usecounter{CTR} == BEGIN @nmbrlist :=L true

                             \@listctr == CTR
                             \setcounter{CTR}{0}

END

DEFINE \dimen's and \count
\topskip
\partopsep
\itemsep
\parsep
\@topsep
\@topsepadd
\outerparskip

1 ⟨∗2ekernel⟩

2 \newskip\topsep
3 \newskip\partopsep
4 \newskip\itemsep
5 \newskip\parsep
6 \newskip\@topsep
7 \newskip\@topsepadd
8 \newskip\@outerparskip
\leftmargin
\rightmargin
\listparindent
\itemindent
\labelwidth
\labelsep
\@totalleftmargin

9 \newdimen\leftmargin
10 \newdimen\rightmargin
11 \newdimen\listparindent
12 \newdimen\itemindent
13 \newdimen\labelwidth
14 \newdimen\labelsep
15 \newdimen\linewidth
16 \newdimen\@totalleftmargin \@totalleftmargin=\z@
\leftmargini
\leftmarginii
\leftmarginiii
\leftmarginiv
\leftmarginv
\leftmarginvi

17 \newdimen\leftmargini

8

18 \newdimen\leftmarginii
19 \newdimen\leftmarginiii
20 \newdimen\leftmarginiv
21 \newdimen\leftmarginv
22 \newdimen\leftmarginvi
\@listdepth
\@itempenalty
\@beginparpenalty
\@endparpenalty

23 \newcount\@listdepth \@listdepth=0
24 \newcount\@itempenalty
25 \newcount\@beginparpenalty
26 \newcount\@endparpenalty
\@labels
27 \newbox\@labels
\if@inlabel
\@inlabelfalse
\@inlabeltrue

28 \newif\if@inlabel \@inlabelfalse
\if@newlist
\@newlistfalse
\@newlisttrue

29 \newif\if@newlist   \@newlistfalse
\if@noparitem
\@noparitemfalse
\@noparitemtrue

30 \newif\if@noparitem \@noparitemfalse
\if@noparlist
\@noparlistfalse
\@noparlisttrue

31 \newif\if@noparlist \@noparlistfalse
\if@noitemarg
\@noitemargfalse
\@noitemargtrue

32 \newif\if@noitemarg \@noitemargfalse
\if@newlist
\@newlistfalse
\@newlisttrue

33 \newif\if@nmbrlist  \@nmbrlistfalse
\list
34 \def\list#1#2{\ifnum \@listdepth >5\relax \@toodeep
35  \else \global\advance\@listdepth\@ne \fi
36 \rightmargin \z@ \listparindent\z@ \itemindent\z@
37 \csname @list\romannumeral\the\@listdepth\endcsname
38 \def\@itemlabel{#1}\let\makelabel\@mklab \@nmbrlistfalse #2\relax
39 \@trivlist
40 \parskip\parsep \parindent\listparindent
41 \advance\linewidth -\rightmargin \advance\linewidth -\leftmargin
42 \advance\@totalleftmargin \leftmargin
43 \parshape \@ne \@totalleftmargin \linewidth
44 \ignorespaces}
\par@deathcycles
45 \newcount\par@deathcycles

\@trivlist
46 \def\@trivlist{\@topsepadd\topsep
47 \if@noskipsec \leavevmode \fi
48 \ifvmode \advance\@topsepadd\partopsep \else \unskip\par\fi
49 \if@inlabel \@noparitemtrue \@noparlisttrue
50  \else \@noparlistfalse \@topsep\@topsepadd \fi
51  \advance\@topsep \parskip
52 \leftskip\z@skip\rightskip\@rightskip \parfillskip\@flushglue
53 \par@deathcycles\z@
54 \@setpar{\if@newlist
55  \advance\par@deathcycles\@ne
56  \ifnum\par@deathcycles>\@m
57  \@noitemerr{\@@par}\fi
58  \else{\@@par}\fi}%
59 \global\@newlisttrue \@outerparskip\parskip}
\trivlist
60 \def\trivlist{\parsep\parskip\@nmbrlistfalse
61 \@trivlist \labelwidth\z@ \leftmargin\z@
62 \itemindent\z@

We initialise \@itemlabel so that a trivlist with an \item not having an optional argument doesn’t produce an error message.

63 \let\@itemlabel\@empty
64 \def\makelabel##1{##1}}
\endlist
65 \def\endlist{\global\advance\@listdepth\m@ne
66  \endtrivlist}

The definition of \trivlist used to be in ltspace.dtx so that other commands could be ‘let to it’. They now use \def.

\endtrivlist
67 \def\endtrivlist{%
68 \if@inlabel\indent\fi
69 \if@newlist\@noitemerr\fi
70 \ifhmode\unskip \par\fi
71 \if@noparlist \else
72  \ifdim\lastskip >\z@
73  \@tempskipa\lastskip \vskip -\lastskip
74  \advance\@tempskipa\parskip \advance\@tempskipa -\@outerparskip
75  \vskip\@tempskipa
76  \fi
77  \@endparenv
78 \fi

79 }

\@endparenv \@doendpe To suppress the paragraph indentation in text immediately following a paragraphmaking environment, \everypar is changed to remove the space, and \par is redefined to restore \everypar. Instead of redefining \par and \everpar, \@endparenv was changed to set the @endpe switch, letting \end redefine \par

and \everypar.

This allows paragraph-making environments to work right when called by other environments. (Changed 27 Oct 86)

80 \def\@endparenv{%
81 \addpenalty\@endparpenalty\addvspace\@topsepadd\@endpetrue}
82 \def\@doendpe{\@endpetrue
83  \def\par{\@restorepar\everypar{}\par\@endpefalse}\everypar
   Use \setbox0=\lastbox instead of \hskip -\parindent so that a \noindent

becomes a no-op when used before a line immediately following a list environment(23 Oct 86).

84  {{\setbox\z@\lastbox}\everypar{}\@endpefalse}}
\if@endpe
\@endpefalse
\@endpeltrue

85 \newif\if@endpe
86 \@endpefalse
\@mklab
87 \def\@mklab#1{\hfil #1}
\item
88 \def\item{\@inmatherr\item
89 \@ifnextchar [\@item{\@noitemargtrue \@item[\@itemlabel]}}
\@donoparitem
90 \def\@donoparitem{\@noparitemfalse
91 \global\setbox\@labels\hbox{\hskip -\leftmargin
92  \unhbox\@labels
93  \hskip \leftmargin}\if@minipage\else
94 \@tempskipa\lastskip
95 \vskip -\lastskip \advance\@tempskipa\@outerparskip
96 \advance\@tempskipa -\parskip \vskip\@tempskipa\fi}
\@item
97 \def\@item[#1]{\if@noparitem \@donoparitem
98 \else \if@inlabel \indent \par \fi
99  \ifhmode \unskip\unskip \par \fi
100  \if@newlist \if@nobreak \@nbitem \else
101  \addpenalty\@beginparpenalty
102  \addvspace\@topsep \addvspace{-\parskip}\fi
103  \else \addpenalty\@itempenalty \addvspace\itemsep
104  \fi
105  \global\@inlabeltrue
106 \fi
107 \everypar{\global\@minipagefalse\global\@newlistfalse
108  \if@inlabel
109  \global\@inlabelfalse

This next command was changed from an hskip to a kern to avoid a break point after the parindent box: the skip can cause a line-break if a very long label occurs in raggedright setting.

110 %           \hskip -\parindent
111  \kern -\parindent
112  \box\@labels
113  \penalty\z@
114  \fi
115  \everypar{}}%
116 \global\@nobreakfalse
117 \if@noitemarg \@noitemargfalse \if@nmbrlist
118  \refstepcounter\@listctr\fi \fi

We use \sbox to support colour commands.

119 \sbox\@tempboxa{\makelabel{#1}}%
120 \global\setbox\@labels
121 \hbox{\unhbox\@labels \hskip \itemindent
122  \hskip -\labelwidth \hskip -\labelsep
123  \ifdim \wd\@tempboxa >\labelwidth
124  \box\@tempboxa
125  \else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
126  \hskip \labelsep}\ignorespaces}
\makelabel
127 \def\makelabel#1{\@latex@error{Lonely \string\item--perhaps a missing
128  list environment}\@ehc}
\@nbitem
129 \def\@nbitem{\@tempskipa\@outerparskip \advance\@tempskipa -\parskip
130  \addvspace\@tempskipa}
\usecounter
131 \def\usecounter#1{\@nmbrlisttrue\def\@listctr{#1}\setcounter{#1}\z@}

1.6 Itemize and Enumerate

Enumeration is done with four counters: enumi, enumii, enumiii and enumiv, where enumN controls the numbering of the Nth level enumeration. The label is generated by the commands \labelenumi … \labelenumiv, which should be defined by the document style. Note that \p@enumN\theenumN defines the output of a \ref command. A typical definition might be:

      \def\theenumii{\alph{enumii}}
      \def\p@enumii{\theenumi}
      \def\labelenumii{(\theenumii)}

which will print the labels as ‘(a)’, ‘(b)’, … and print a \ref as ‘3a’.

The item numbers are moved to the right of the label box, so they are always a distance of \labelsep from the item.

\@enumdepth holds the current enumeration nesting depth.

Itemization is controlled by four commands: \labelitemi, \labelitemii, \labelitemiii, and \labelitemiv. To cause the second-level list to be bulleted,

you just define \labelitemii to be •. \@itemspacing and \@itemdepth are the
analogs of \@enumspacing and \@enumdepth.
 \enumerate ==

BEGIN

     if \@enumdepth > 3

then errormessage: “Too deeply nested”.

       else \@enumdepth :=L \@enumdepth + 1
            \@enumctr :=L eval(enum@\romannumeral\the\@enumdepth)
            \list{\label(\@enumctr)}
                 {\usecounter{\@enumctr}
                  \makelabel{LABEL} == \hss \llap{LABEL}}

fi END

 \endenumerate == \endlist
\@enumdepth
132 \newcount\@enumdepth \@enumdepth = 0
\c@enumi \c@enumii \c@enumii \c@enumiv
133 \@definecounter{enumi}
134 \@definecounter{enumii}
135 \@definecounter{enumiii}
136 \@definecounter{enumiv}
enumerate
137 \def\enumerate{%
138 \ifnum \@enumdepth >\thr@@\@toodeep\else
139  \advance\@enumdepth\@ne
140  \edef\@enumctr{enum\romannumeral\the\@enumdepth}%
141  \expandafter
142  \list
143  \csname label\@enumctr\endcsname
144  {\usecounter\@enumctr\def\makelabel##1{\hss\llap{##1}}}%
145 \fi}
146 \let\endenumerate =\endlist
  \itemize ==

BEGIN

      if \@itemdepth > 3

then errormessage: ’Too deeply nested’.

         else \@itemdepth :=L \@itemdepth + 1
              \@itemitem ==
 eval(labelitem\romannumeral\the\@itemdepth)
              \list{\@nameuse{\@itemitem}}
                    {\makelabel{LABEL} == \hss \llap{LABEL}}

fi END

   \enditemize == \endlist

\@itemdepth
147 \newcount\@itemdepth \@itemdepth = 0
itemize
148 \def\itemize{%
149 \ifnum \@itemdepth >\thr@@\@toodeep\else
150  \advance\@itemdepth\@ne
151  \edef\@itemitem{labelitem\romannumeral\the\@itemdepth}%
152  \expandafter
153  \list
154  \csname\@itemitem\endcsname
155  {\def\makelabel##1{\hss\llap{##1}}}%
156 \fi}
157 \let\enditemize =\endlist

158 ⟨/2ekernel⟩