1

I've read a lot of times that you shouldn't reinvent the wheel, but sometimes you have to. I'm trying to create an enumerate style mylist environment, based on the base list environment provided by LaTeX without using the enumitem package. I can handle the basic parameters, but, I can't understand the nesting for levels.

By way of comparison, I want to get something like:

expected

The code I have is the following:

\documentclass{article}
\usepackage{xparse}
\usepackage{enumitem}
\setlist[enumerate,1]{label= \arabic*., left=0pt, labelsep=5pt, itemsep=4pt, partopsep=2pt}
\setlist[enumerate,2]{leftmargin=16pt, itemsep=0pt, nosep, label=\Alph*)}
\setlength{\parindent}{0pt}

\ExplSyntaxOn
\newcounter{mycount}
\cs_new_eq:NN \__mylist_start_list:nn \list
\cs_new_eq:NN \__mylist_stop_list: \endlist
\tl_new:N \l_mylist_label_tl

\keys_define:nn { mylist }
  {
    label         .code:n    = \tl_set:Nn \l_tmpa_tl {#1}
                               \tl_put_right:Nx \l_mylist_label_tl
                                 { \exp_not:V \l_tmpa_tl },
    label         .initial:n = \arabic{mycount}.,
    itemsep       .dim_set:N = \l_mylist_itemsep_dim,
    itemsep       .initial:n = 0pt,
    itemindent    .dim_set:N = \l_mylist_itemindent_dim,
    itemindent    .initial:n = 0in,
    labelsep      .dim_set:N = \l_mylist_labelsep_dim,
    labelsep      .initial:n = 0.5em,
    leftmargin    .dim_set:N = \l_mylist_leftmargin_dim,
    leftmargin    .initial:n = 0in,
    rightmargin   .dim_set:N = \l_mylist_rightmargin_dim,
    rightmargin   .initial:n = 0pt,
    labelwidth    .dim_set:N = \l_mylist_labelwidth_dim,
    labelwidth    .initial:n =  \l_mylist_leftmargin_dim - \l_mylist_labelsep_dim,
    topsep        .dim_set:N = \l_mylist_topsep_dim,
    topsep        .initial:n = -0.3ex,
    parsep        .dim_set:N = \l_mylist_parsep_dim,
    parsep        .initial:n = 0pt,
    partopsep     .dim_set:N = \l_mylist_partopsep_dim,
    partopsep     .initial:n = 2pt,
    listparindent .dim_set:N = \l_mylist_listparindent_dim,
    listparindent .initial:n = 0.5in,
  }

\NewDocumentEnvironment{mylist}{ O{} }
  {
    \IfNoValueF{#1}{ \keys_set:nn { mylist } {#1} }
    \__mylist_start_list:nn
      { \tl_use:N \l_mylist_label_tl }
      {
        \usecounter{mycount}
        \dim_set:Nn \itemsep       { \l_mylist_itemsep_dim       }
        \dim_set:Nn \itemindent    { \l_mylist_itemindent_dim    }
        \dim_set:Nn \labelsep      { \l_mylist_labelsep_dim      }
        \dim_set:Nn \labelwidth    { \l_mylist_labelwidth_dim    }
        \dim_set:Nn \leftmargin    { \l_mylist_leftmargin_dim    }
        \dim_set:Nn \rightmargin   { \l_mylist_rightmargin_dim   }
        \dim_set:Nn \topsep        { \l_mylist_topsep_dim        }
        \dim_set:Nn \parsep        { \l_mylist_parsep_dim        }
        \dim_set:Nn \partopsep     { \l_mylist_partopsep_dim     }
        \dim_set:Nn \listparindent { \l_mylist_listparindent_dim }
      }
  }
  {
    \__mylist_stop_list:
  }
\ExplSyntaxOff

\begin{document}
XXXX
\hrule
\begin{enumerate}
\item Using enumerate redefined by enumitem pkg
  \begin{enumerate}
    \item value second level
    \item value second level
    \item value second level
  \end{enumerate}
\end{enumerate}
\hrule
YYY
\begin{mylist}[leftmargin=0cm]
\item Using mylist environment nested here:
  \begin{mylist}
    \item value second level
    \item value second level
    \item value second level
  \end{mylist}
\end{mylist}
\hrule
\end{document}

I don't know how to create and access the different levels of nesting as enumerated.

First attempt: Adding the counters and detecting the nesting level.

\documentclass{article}
\usepackage{xparse}
\usepackage{enumitem}
\setlist[enumerate,1]{label= \arabic*., left=0pt, labelsep=5pt, itemsep=4pt, partopsep=2pt}
\setlist[enumerate,2]{leftmargin=16pt, itemsep=0pt, nosep, label=\Alph*)}
\setlength{\parindent}{0pt}

\ExplSyntaxOn
\newcounter{mycounti}
\newcounter{mycountii}
\newcounter{mycountiii}
\int_new:N \l_mylist_level_int
\cs_new_eq:NN \__mylist_start_list:nn \list
\cs_new_eq:NN \__mylist_stop_list: \endlist
\tl_new:N \l_mylist_label_i_tl
\tl_new:N \l_mylist_label_ii_tl
\tl_new:N \l_mylist_label_iii_tl

% topsep partopsep parsep itemsep
\cs_new_protected:Npn \__mylist_vspace_keys:nnnnn #1#2#3#4#5
  {
    \keys_define:nn { mylist/level-#1 }
      {
        topsep        .dim_set:c = { l_mylist_topsep_#1_dim },
        topsep        .initial:n = {#2},
        partopsep     .dim_set:c = { l_mylist_partopsep_#1_dim },
        partopsep     .initial:n = {#3},
        parsep        .dim_set:c = { l_mylist_parsep_#1_dim },
        parsep        .initial:n = {#4},
        itemsep       .dim_set:c = { l_mylist_itemsep_#1_dim },
        itemsep       .initial:n = {#5},
      }
  }
\__mylist_vspace_keys:nnnnn { i  }{ 0cm }{ 0cm }{ 0cm }{ 0mm }
\__mylist_vspace_keys:nnnnn { ii }{ 0cm }{ 0cm }{ 0cm }{ 0mm }

% leftmargin rightmargin listparindent labelwidth labelsep itemindent
\cs_new_protected:Npn \__mylist_hspace_keys:nnnnnnn #1#2#3#4#5#6#7
  {
    \keys_define:nn { mylist/level-#1 }
      {
        leftmargin    .dim_set:c = { l_mylist_leftmargin_#1_dim },
        leftmargin    .initial:n = {#2},
        rightmargin   .dim_set:c = { l_mylist_rightmargin_#1_dim },
        rightmargin   .initial:n = {#3},
        listparindent .dim_set:c = { l_mylist_listparindent_#1_dim },
        listparindent .initial:n = {#4},
        labelwidth    .dim_set:c = { l_mylist_labelwidth_#1_dim },
        labelwidth    .initial:n = {#5},
        labelsep      .dim_set:c = { l_mylist_labelsep_#1_dim },
        labelsep      .initial:n = {#6},
        itemindent    .dim_set:c = { l_mylist_itemindent_#1_dim },
        itemindent    .initial:n = {#7},
      }
  }
\__mylist_hspace_keys:nnnnnnn {i }{ 0cm }{ 0cm }{ 0cm }{ 0.5in }{ 0.5em }{ 0cm }
\__mylist_hspace_keys:nnnnnnn {ii}{ 1cm }{ 0cm }{ 16pt }{ 0cm }{ 0.25cm }{ 0cm }

\cs_new_protected:Npn \__mylist_level_i_defns:
  {
    \usecounter{mycounti}
    \dim_set:Nn \itemsep       { \l_mylist_itemsep_i_dim       }
    \dim_set:Nn \itemindent    { \l_mylist_itemindent_i_dim    }
    \dim_set:Nn \labelsep      { \l_mylist_labelsep_i_dim      }
    \dim_set:Nn \labelwidth    { \l_mylist_labelwidth_i_dim    }
    \dim_set:Nn \leftmargin    { \l_mylist_leftmargin_i_dim  }
    \dim_set:Nn \rightmargin   { \l_mylist_rightmargin_i_dim   }
    \dim_set:Nn \topsep        { \l_mylist_topsep_i_dim        }
    \dim_set:Nn \parsep        { \l_mylist_parsep_i_dim        }
    \dim_set:Nn \partopsep     { \l_mylist_partopsep_i_dim     }
    \dim_set:Nn \listparindent { \l_mylist_listparindent_i_dim }
  }

\cs_new_protected:Npn \__mylist_level_ii_defns:
  {
    \usecounter{mycountii}
    \dim_set:Nn \itemsep       { \l_mylist_itemsep_ii_dim       }
    \dim_set:Nn \itemindent    { \l_mylist_itemindent_ii_dim    }
    \dim_set:Nn \labelsep      { \l_mylist_labelsep_ii_dim      }
    \dim_set:Nn \labelwidth    { \l_mylist_labelwidth_ii_dim    }
    \dim_set:Nn \leftmargin    { \l_mylist_leftmargin_ii_dim  }
    \dim_set:Nn \rightmargin   { \l_mylist_rightmargin_ii_dim   }
    \dim_set:Nn \topsep        { \l_mylist_topsep_ii_dim        }
    \dim_set:Nn \parsep        { \l_mylist_parsep_ii_dim        }
    \dim_set:Nn \partopsep     { \l_mylist_partopsep_ii_dim     }
    \dim_set:Nn \listparindent { \l_mylist_listparindent_ii_dim }
  }

% label
\keys_define:nn { mylist/level-i }
  {
    label .code:n    = \tl_set:Nn \l_tmpa_tl {#1}
                       \tl_put_right:Nx \l_mylist_label_i_tl { \exp_not:V \l_tmpa_tl },
    label .initial:n = \arabic{mycounti}.,
  }

\keys_define:nn { mylist/level-ii }
  {
    label .code:n    = \tl_set:Nn \l_tmpa_tl {#1}
                       \tl_put_right:Nx \l_mylist_label_ii_tl { \exp_not:V \l_tmpa_tl },
    label .initial:n = \Alph{mycountii}),
  }

\NewDocumentEnvironment{mylist}{ O{} }
  {
    \int_incr:N \l_mylist_level_int
    \IfNoValueF{#1}
      {
        \int_case:nn { \l_mylist_level_int }
          {
            {1} { \keys_set:nn { mylist / level-i  } {#1} }
            {2} { \keys_set:nn { mylist / level-ii } {#1} }
          }
      }
    \__mylist_start_list:nn
      {
        \int_case:nn { \l_mylist_level_int }
          {
            {1} { \tl_use:N \l_mylist_label_i_tl  }
            {2} { \tl_use:N \l_mylist_label_ii_tl }
          }
      }
      {
        \def\makelabel##1{\hss\llap{##1}} % ugly :(
        \use:c { __mylist_level_ \int_to_roman:n { \l_mylist_level_int } _defns: }
      }
  }
  { \__mylist_stop_list: }

\ExplSyntaxOff

\begin{document}
XXXX
\hrule
\begin{enumerate}
\item Using enumerate redefined by enumitem pkg
  \begin{enumerate}
    \item value second level
    \item value second level
    \item value second level
  \end{enumerate}
\end{enumerate}
\hrule
YYY
\begin{mylist}
\item Using mylist environment nested here:
  \begin{mylist}
    \item value second level
    \item value second level
    \item value second level
  \end{mylist}
\end{mylist}
\hrule
\end{document}

Second attempt: Now closer to what I want and with more defined levels.

\documentclass{article}
\usepackage{xparse}
\usepackage{enumitem}
\setlist[enumerate,1]{label= \arabic*., left=0pt, labelsep=5pt, itemsep=4pt, partopsep=2pt}
\setlist[enumerate,2]{leftmargin=16pt, itemsep=0pt, nosep, label=\Alph*)}
\setlength{\parindent}{0pt}

\ExplSyntaxOn
\newcounter{mycounti}
\newcounter{mycountii}
\newcounter{mycountiii}
\int_new:N \l_mylist_level_int
\cs_new_eq:NN \__mylist_start_list:nn \list
\cs_new_eq:NN \__mylist_stop_list: \endlist
\tl_new:N \l_mylist_label_i_tl
\tl_new:N \l_mylist_label_ii_tl
\tl_new:N \l_mylist_label_iii_tl

% topsep partopsep parsep itemsep
\cs_new_protected:Npn \__mylist_vspace_keys:nnnnn #1#2#3#4#5
  {
    \keys_define:nn { mylist / level-#1 }
      {
        topsep        .dim_set:c = { l_mylist_topsep_#1_dim },
        topsep        .initial:n = {#2},
        partopsep     .dim_set:c = { l_mylist_partopsep_#1_dim },
        partopsep     .initial:n = {#3},
        parsep        .dim_set:c = { l_mylist_parsep_#1_dim },
        parsep        .initial:n = {#4},
        itemsep       .dim_set:c = { l_mylist_itemsep_#1_dim },
        itemsep       .initial:n = {#5},
      }
  }
\__mylist_vspace_keys:nnnnn { i   }{ 0cm }{ 0cm }{ 0cm }{ 0mm }
\__mylist_vspace_keys:nnnnn { ii  }{ 0cm }{ 0cm }{ 0cm }{ 0mm }
\__mylist_vspace_keys:nnnnn { iii }{ 0cm }{ 0cm }{ 0cm }{ 0mm }

% rightmargin listparindent itemindent labelwidth labelsep (no leftmargin here)
\cs_new_protected:Npn \__mylist_hspace_keys:nnnnnn #1#2#3#4#5#6%#7
  {
    \keys_define:nn { mylist / level-#1 }
      {
        rightmargin   .dim_set:c = { l_mylist_rightmargin_#1_dim },
        rightmargin   .initial:n = {#2},
        listparindent .dim_set:c = { l_mylist_listparindent_#1_dim },
        listparindent .initial:n = {#3},
        itemindent    .dim_set:c = { l_mylist_itemindent_#1_dim },
        itemindent    .initial:n = {#4},
        labelwidth    .dim_set:c = { l_mylist_labelwidth_#1_dim },
        labelwidth    .initial:n = {#5},
        labelsep      .dim_set:c = { l_mylist_labelsep_#1_dim },
        labelsep      .initial:n = {#6},
        %leftmargin    .dim_set:c = { l_mylist_leftmargin_#1_dim },
        %leftmargin    .initial:n = {#7},
      }
  }
\__mylist_hspace_keys:nnnnnn { i   }{ 0cm }{ 0cm }{ 0cm }{ 1.25em }{ 0.50em }%{ 1.75em }
\__mylist_hspace_keys:nnnnnn { ii  }{ 0cm }{ 0cm }{ 0cm }{ 1.25em }{ 0.50em }%{ 1.75em }
\__mylist_hspace_keys:nnnnnn { iii }{ 0cm }{ 0cm }{ 0cm }{ 1.25em }{ 0.50em }%{ 1.75em }

\cs_new_protected:Npn \__mylist_level_i_defns:
  {
    \usecounter{mycounti}
    \dim_set:Nn \itemsep       { \l_mylist_itemsep_i_dim       }
    \dim_set:Nn \itemindent    { \l_mylist_itemindent_i_dim    }
    \dim_set:Nn \labelsep      { \l_mylist_labelsep_i_dim      }
    \dim_set:Nn \labelwidth    { \l_mylist_labelwidth_i_dim    }
    \dim_set:Nn \leftmargin    { \l_mylist_leftmargin_i_dim    }
    \dim_set:Nn \rightmargin   { \l_mylist_rightmargin_i_dim   }
    \dim_set:Nn \topsep        { \l_mylist_topsep_i_dim        }
    \dim_set:Nn \parsep        { \l_mylist_parsep_i_dim        }
    \dim_set:Nn \partopsep     { \l_mylist_partopsep_i_dim     }
    \dim_set:Nn \listparindent { \l_mylist_listparindent_i_dim }
  }

\cs_new_protected:Npn \__mylist_level_ii_defns:
  {
    \usecounter{mycountii}
    \dim_set:Nn \itemsep       { \l_mylist_itemsep_ii_dim       }
    \dim_set:Nn \itemindent    { \l_mylist_itemindent_ii_dim    }
    \dim_set:Nn \labelsep      { \l_mylist_labelsep_ii_dim      }
    \dim_set:Nn \labelwidth    { \l_mylist_labelwidth_ii_dim    }
    \dim_set:Nn \leftmargin    { \l_mylist_leftmargin_ii_dim    }
    \dim_set:Nn \rightmargin   { \l_mylist_rightmargin_ii_dim   }
    \dim_set:Nn \topsep        { \l_mylist_topsep_ii_dim        }
    \dim_set:Nn \parsep        { \l_mylist_parsep_ii_dim        }
    \dim_set:Nn \partopsep     { \l_mylist_partopsep_ii_dim     }
    \dim_set:Nn \listparindent { \l_mylist_listparindent_ii_dim }
  }

\cs_new_protected:Npn \__mylist_level_iii_defns:
  {
    \usecounter{mycountiii}
    \dim_set:Nn \itemsep       { \l_mylist_itemsep_iii_dim       }
    \dim_set:Nn \itemindent    { \l_mylist_itemindent_iii_dim    }
    \dim_set:Nn \labelsep      { \l_mylist_labelsep_iii_dim      }
    \dim_set:Nn \labelwidth    { \l_mylist_labelwidth_iii_dim    }
    \dim_set:Nn \leftmargin    { \l_mylist_leftmargin_iii_dim    }
    \dim_set:Nn \rightmargin   { \l_mylist_rightmargin_iii_dim   }
    \dim_set:Nn \topsep        { \l_mylist_topsep_iii_dim        }
    \dim_set:Nn \parsep        { \l_mylist_parsep_iii_dim        }
    \dim_set:Nn \partopsep     { \l_mylist_partopsep_iii_dim     }
    \dim_set:Nn \listparindent { \l_mylist_listparindent_iii_dim }
  }

% Set label and leftmargin
\keys_define:nn { mylist / level-i }
  {
    label      .code:n    = \tl_set:Nn \l_tmpa_tl {#1}
                            \tl_put_right:Nx \l_mylist_label_i_tl { \exp_not:V \l_tmpa_tl },
    label      .initial:n = \arabic{mycounti}.,
    leftmargin .dim_set:c = { l_mylist_leftmargin_i_dim },
    leftmargin .initial:n = {1.75em},
  }

\keys_define:nn { mylist / level-ii }
  {
    label      .code:n    = \tl_set:Nn \l_tmpa_tl {#1}
                            \tl_put_right:Nx \l_mylist_label_ii_tl { \exp_not:V \l_tmpa_tl },
    label      .initial:n = \Alph{mycountii}),
    leftmargin .dim_set:c = { l_mylist_leftmargin_ii_dim },
    leftmargin .initial:n = {1.75em},
  }

\keys_define:nn { mylist / level-iii }
  {
    label      .code:n    = \tl_set:Nn \l_tmpa_tl {#1}
                            \tl_put_right:Nx \l_mylist_label_iii_tl { \exp_not:V \l_tmpa_tl },
    label      .initial:n = \roman{mycountiii}.,
    leftmargin .dim_set:c = { l_mylist_leftmargin_iii_dim },
    leftmargin .initial:n = {1.75em},
  }

\NewDocumentEnvironment{mylist}{ O{} }
  {
    \int_incr:N \l_mylist_level_int
    \IfNoValueF{#1}
      {
        \int_case:nn { \l_mylist_level_int }
          {
            {1} { \keys_set:nn { mylist / level-i   } {#1} }
            {2} { \keys_set:nn { mylist / level-ii  } {#1} }
            {3} { \keys_set:nn { mylist / level-iii } {#1} }
          }
      }
    \__mylist_start_list:nn
      {
        \int_case:nn { \l_mylist_level_int }
          {
            {1} { \tl_use:N \l_mylist_label_i_tl  }
            {2} { \tl_use:N \l_mylist_label_ii_tl }
            {3} { \tl_use:N \l_mylist_label_iii_tl }
          }
      }
      {
        \use:c { __mylist_level_ \int_to_roman:n { \l_mylist_level_int } _defns: }
      }
  }
  { \__mylist_stop_list: }
\ExplSyntaxOff
\begin{document}
XXXX
\hrule
\begin{enumerate}
\item Using enumerate redefined by enumitem pkg
  \begin{enumerate}
    \item value second level
    \item value second level
    \item value second level
  \end{enumerate}
\end{enumerate}
\hrule
YYY
\begin{mylist}[itemsep=4pt, parsep=2pt]
\item Using mylist environment nested here:
  \begin{mylist}
    \item value second level
    \item value second level
    \item value second level
  \end{mylist}
\end{mylist}
\hrule
\end{document}

newoutput

I have not been able to use the same values as in enumitem and the use of leftmargin is a problem, but, it is quite (or very) close to what I want. I don't know if the code is completely correct, but it is functional.

7
  • At least you need several counters: one for each level and one to take care of the nesting level. You also need to add the \@totalleftmargin when going to a further level. – egreg Oct 2 '20 at 8:31
  • The counters I understand (that's not complicated), the macro '\@totalleftmargin' I know where to place it along with the counter for the nesting level, can you give me an example? – Pablo González L Oct 2 '20 at 11:44
  • Actually it should be \list that by itself takes care of it. Forget about it. – egreg Oct 2 '20 at 11:48
  • Ok, I have to create a \tl_var for each level and associate it to the nesting counter? – Pablo González L Oct 2 '20 at 12:06
  • 4
    I’m voting to close this question because the OP has requested it in a comment. – barbara beeton Oct 11 '20 at 15:47

Browse other questions tagged or ask your own question.