2

Given content of several paragraphs (which definitely has at least one occurrence of {MyList} environment), I would like to define a macro that can break this content into the content before the end of the nth item of {MyList} and the content after the nth item.

That is, given input of the form

Some text before
\begin{MyList}
  \item First.
  \item Second.
  \item Third.
\end{MyList}
Some text after

transform this into (assuming n=1)

Some text before
\begin{MyList}
  \item First.
\end{MyList}%          end list after n items
%% -----------
\begin{MyList}%        restart list after n items
  \item Second.
  \item Third.
\end{MyList}
Some text after

and process the two pieces separately.

If n=2, then the desired transformation would be

Some text before
\begin{MyList}
  \item First.
  \item Second.
\end{MyList}%          end list after n items
%% -----------
\begin{MyList}%        restart list after n items
  \item Third.
\end{MyList}
Some text after

Ideally, I would like to provide an integer to indicate the number of line items to break at. But, in case there does not exist sufficient TeX magic to do this, one option would to require a \MarkBoundaryPoint to be placed in the list to denote the point where the break is to occur.
That is included in the MWE, but is a NOOP for now.

If that is still not possible, what is the least markup that would allow this to be done?

Notes:


The desired output of the MWE, after appropriate changes to \IncludeUpToFirstNItemsOfMyList and \IncludeAfterFirstNItemsOfMyList would be:

enter image description here

Code:

\documentclass{article}
\usepackage{enumitem}

%% This list is ALWAYS resumed.
%% https://tex.stackexchange.com/questions/366179/always-resumed-list-does-not-always-resume-if-invoked-from-within-an-environment
\newlist{MyList}{enumerate}{2}
\setlist[MyList,1]{
  label=\arabic*.,
  before=\setcounter{MyListi}{\value{MyList}},
  after=\setcounter{MyList}{\value{MyListi}},
}% ALWAYS resumed
\setlist[MyList,2]{label=\alph*)}
\newcounter{MyList}


\newcommand{\IncludeUpToFirstNItemsOfMyList}[2][1]{%
    %% #1 = number of list item to put in fbox
    %% #2 = content which includes a {MyList}
    %%
    %% ???  How do I ignore ALL text AFTER first n items? 
    #2% TBD: This macro should cutoff any text AFTER item number specified in #1
}%
\newcommand{\IncludeAfterFirstNItemsOfMyList}[2][1]{%
    %% #1 = number of list item to discard
    %% #2 = content which includes a {MyList}
    %%
    %% ???  How do I ignore ALL text up to the first n items? 
    #2% TBD: This macro should cutoff any text BEFORE item number specified in #1
}%

\newcommand*{\MarkBoundaryPoint}{}% 2nd option (in case it is not possible to do without)

\newcommand{\PutPartOfListInFbox}[2][1]{%
    %% #1 = number of list item to put in fbox
    %% #2 = content which includes a {MyList}
    %%
    \noindent
    \fboxsep=0pt
    \fbox{%
        %% This is to test this capability and display it to be able to see that it
        %% worked as desired.
       \begin{minipage}{0.5\linewidth}
            \IncludeUpToFirstNItemsOfMyList[#1]{#2}%
        \end{minipage}%
    }%

    \IncludeAfterFirstNItemsOfMyList[#1]{#2}%
}

\newcommand*{\MyContent}{%
    Some optional content before the list.
    \begin{MyList}
        \item First Item of list. This will be nonempty, but may or may not have more than
            one paragraph.

            Second optional para of first Item.\MarkBoundaryPoint
        \item Second Item is optional.  
            Could be zero (in which case this item would not exist)
            or more paragraphs.

            Second optional para of second Item.
        \item Third Item is optional.

            Second optional para of third Item.
    \end{MyList}%
    Some optional content after the list.
}%

\begin{document}
    \PutPartOfListInFbox{\MyContent}
\end{document}
2

Here is one approach.

  • Before every \item, we check the current value of MyListi. If it is equal to some threshold, given via \MyListSeparateAfter, we split the list there.
  • Some content can be inserted between the two list parts, given via \MyListInsertBetween.
  • Boxed draws a box around its content. You can specify the desired width of the box (\linewidth by default) as well as the \fbox parameters.
  • MyListBoxed does the boxing you want (I think). As in your example, I drew the box with no padding, though I think some should be added.
  • Depending on your application, some \hrules (or similar) may be better than a box. This would allow page breaks and not introduce any complicating horizontal spacing issues.
\documentclass{article}

\usepackage{etoolbox}
\usepackage{xparse}
\usepackage{enumitem}

\makeatletter
  \newlist{MyList}{enumerate}{2}
  % We want out list to always resume, so we store the current list, so we store
  % the current value in the counter MyList at the end of each list and retreave
  % it at the beginning.
  % We also add a hook to the start of \item which can be used to split the list.
  \setlist[MyList,1]{
    label=\arabic*.,
    before={%
        \setcounter{MyListi}{\value{MyList}}%
        \preto\item{\MyListSeparate@maybe}%
      },
    after=\setcounter{MyList}{\value{MyListi}},
  }
  \setlist[MyList,2]{label=\alph*)}
  \newcounter{MyList}
  % If we are at level 1 of the list and the value of the counter has reached
  % the threshold, we remove the threshold and insert \MyListSeparate.
  \newcommand*\MyListSeparate@maybe{%
    \ifnum\enit@depth=1\relax
      \ifnum\MyList@separate@threshold=\value{MyListi}\relax
        \MyListSeparateAfter{\MyList@separate@threshold@false}%
        \expandafter\expandafter
        \expandafter\MyListSeparate
      \fi
    \fi
  }
  % This command separates the list.
  \newcommand*\MyListSeparate{%
    \end{MyList}%
    \MyList@between
    \begin{MyList}%
  }
  % Command for setting the threshold.
  \newcommand*\MyListSeparateAfter[1]{%
    \xdef\MyList@separate@threshold{#1}%
  }
  % The threshold will be used in an \ifnum as the first number to compare. This
  % default value makes the comparison false regardless of the second number.
  \def\MyList@separate@threshold@false{0<0}
  \MyListSeparateAfter{\MyList@separate@threshold@false}
  % Command for setting the code to insert between the list parts.
  \newcommand\MyListInsertBetween[1]{%
    \def\MyList@between{#1}%
  }
  \MyListInsertBetween{}
  % A wrapper to capture and box any content (does not allow page breaks).
  \NewDocumentEnvironment{Boxed}{
    D||{\linewidth} % width of the \fbox
    o % value for \fboxsep
    o % value for \fboxrule
  }{%
    % Adjust the \fbox parameters.
    \IfValueT{#2}{%
      \fboxsep=#2\relax
    }
    \IfValueT{#3}{%
      \fboxrule=#3\relax
    }
    \par\noindent
    % Start a box to save the environment content in. It has to be narrower than
    % \linewidth in order to account for the \fbox that will be added afterwards.
    \setbox\@tempboxa=\hbox\bgroup
    \begin{minipage}[t]{\dimexpr#1-2\fboxsep-2\fboxrule\relax}%
  }{%
    \end{minipage}%
    \egroup
    \fbox{\usebox\@tempboxa}%
    \par
  }
  % An environment that automatically splits the list after a given item and
  % puts the first part in a box. If no threshold or a too large one is given,
  % the whole list is boxed. If a too small threshold is given, no box is drawn.
  \NewDocumentEnvironment{MyListBoxed}{
    O{\MyList@separate@threshold@false} % The splitting threshold
    +O{}    % Code to insert between the box and the second list part.
    +D||{}  % Code to insert before the list, inside the box.
  }{%
    % Store the argument values in the appropriate hooks.
    \MyListSeparateAfter{#1}%
    \MyListInsertBetween{#2}%
    % Check if the threshold is too small.
    \ifnum\MyList@separate@threshold<\numexpr\value{MyList}+1\relax
      % If so, remove the threshold, insert the before-text and start the list.
      \MyListSeparateAfter{\MyList@separate@threshold@false}%
      #3%
      \begin{MyList}%
    \else
      % If not, start a Boxed environment, insert the before-text and start the list.
      \begin{Boxed}[0pt]%
      \def\@tempa{\end{Boxed}}%
      #3%
      \begin{MyList}%
      % Now we tell TeX to insert \@tempa after the end of the current group,
      % which is at \end{MyList}. If the threshold is not met within the list,
      % it is the one inserted by \end{MyListBoxed}.
      \aftergroup\@tempa
    \fi
  }{%
    \end{MyList}%
  }
\makeatother

\begin{document}

We can do it manually:
\MyListSeparateAfter{3}
\MyListInsertBetween{Hello}
\begin{MyList}
  \item one
  \item two
  \item three
  \item four
  \item five
\end{MyList}

Or we can automate it somewhat:
\begin{MyListBoxed}[7]|Some optional content before the list.|
  \item First Item of list. This will be nonempty, but may or may not have more than
    one paragraph.

    Second optional para of first Item.

  \item Second Item is optional.  
    Could be zero (in which case this item would not exist)
    or more paragraphs.

    Second optional para of second Item.
    \begin{MyList}
      \item foo
      \item bar
    \end{MyList}

  \item Third Item is optional.

    Second optional para of third Item.

  \item Four
    \begin{MyList}
      \item tic
      \item toc
    \end{MyList}

\end{MyListBoxed}
Some optional content after the list.

\end{document}

MWE output


If you want to box the second part of the list as well, as you wrote in the comments, you can easily adapt the definition of MyListBoxed to do that.

% An environment that automatically splits the list after a given item and
% puts each part in an \fbox. If the threshold is not given or not in the
% range of the list,  the whole list is boxed (and the between-text thrown
% away).
\NewDocumentEnvironment{MyListBoxed}{
  O{\MyList@separate@threshold@false} % The splitting threshold
  +D||{}  % Code to insert before the list, inside the first box.
  +O{}    % Code to insert between the boxes.
  +D||{}  % Code to insert after the list, inside the second box.
}{%
  % Store the argument values in the appropriate hooks.
  \MyListSeparateAfter{#1}%
  \MyListInsertBetween{\end{Boxed}#3\begin{Boxed}}%
  \def\@tempa{#4}%
  % Check if the threshold is the current counter value.
  \ifnum\MyList@separate@threshold=\numexpr\value{MyList}\relax
    % If so, remove the threshold.
    \MyListSeparateAfter{\MyList@separate@threshold@false}%
  \fi
  % Start a Boxed environment, insert the before-text and start the list.
  \begin{Boxed}[0pt]%
    #2%
    \begin{MyList}%
}{%
    \end{MyList}%
    \@tempa
  \end{Boxed}%
}

Note that I changed the arguments a bit.

\begin{MyListBoxed}[7]
    |Some optional content before the lists.|
    [Some optional content between the lists.]
    |Some optional content after the lists.|
  <list \items>
\end{MyListBoxed}
  • This looks promising. But, I think there is an issue with the code as I don't think the \item blub is executed. – Peter Grill Jul 30 at 0:23
  • @PeterGrill Oops, forgot to clean that up. That case is executed when you try to box up to an item with a number smaller than the first item in MyListBoxed (in the example, 5 or lower). I was not sure about those edge cases; I decided not to draw any box in that case and to box the whole list if no threshold is given. That line was residue from testing, I removed it. – schtandard Jul 30 at 1:00
  • @PeterGrill I added some comments that will hopefully make the code clearer. – schtandard Jul 30 at 11:04
  • I want to put the content after the threshold into another box: tempboxb. I thought I made the appropriate changes without issuing a \usebox{tempboxb} and the content is still displayed for some reason. For the MWE purposes you can just include a \usebox{tempboxb} after the last \end{MyList}. – Peter Grill Jul 30 at 19:57
  • I separated the drawing of the box into a separate environment, Boxed, which should make it a bit easier to modify the environment, and appended an example boxing both parts of the list to the answer. – schtandard Jul 31 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.