% \iffalse meta-comment % % getitems.dtx % Copyright 2016 by Anders O.F. Hendrickson (anders.hendrickson@snc.edu) % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Anders O.F. Hendrickson. % % This work consists of the files moodle.dtx and moodle.ins % and the derived files moodle.sty and getitems.sty. % % \fi % % \iffalse %<*driver> \ProvidesFile{getitems.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{getitems} %<*package> [2016/01/11 v1.0 gather items from a list] % % %<*driver> \documentclass{ltxdoc} \usepackage{getitems} \usepackage{fancyvrb} \usepackage{amssymb} \usepackage{xcolor} \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{getitems.dtx} \PrintChanges %\PrintIndex \end{document} % % \fi % % \CheckSum{212} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{v1.0}{2016/01/11}{Initial version} % % \GetFileInfo{getitems.sty} % % \DoNotIndex{\newcommand,\newenvironment,\def} % % % \title{The \textsf{getitems} package: \\ % gathering {\tt\string\item}'s from a list-like environment\thanks{This document % corresponds to \textsf{getitems.sty}~\fileversion, dated \filedate.}} % \author{Anders Hendrickson\\ St.~Norbert College, De~Pere, WI, USA \\ \texttt{anders.hendrickson@snc.edu}} % % % \maketitle % % \section{Overview} % % The |enumerate| and |itemize| environments of \LaTeX\ organize their contents % through the use of the |\item| command. Each entry in these lists is prefaced % with the command |\item|, making for very compact and easily readable source % code. Package designers may find it useful to use the same syntax for % their custom environments. The \textsf{getitems} package makes % it easy to code such environments by parsing a string of tokens, separating them % by the occurrence of |\item|'s, and saving the contents as macros. % Nested environments are handled correctly. % % Moreover, some typesetting tasks naturally consist of a ``header'' followed by % several related items; one example would be a multiple-choice question on a % school examination. This package saves any \TeX\ tokens appearing before the % first |\item| as the zeroth item for special handling. % % \section{Usage} % % \DescribeMacro{\gatheritems} % To parse a string of text, such as the body of an environment, % call % \begin{center} % |\gatheritems|\marg{text to parse}. % \end{center} % This will scan through the \meta{text to parse}, dividing it at each |\item| % while respecting \TeX\ groupings and nested environments, % and store the divided portions of text into memory. % % \DescribeMacro{numgathereditems} % The total number of items in the parsed text is stored in the \LaTeX\ counter % |numgathereditems|. % % \DescribeMacro{\gathereditem} % To retrieve a stored item, you may call |\gathereditem|\marg{item number}; % the \meta{item number} should expand to an arabic representation of a nonnegative integer. % Any tokens occurring before the first |\item| may be retrieved with % |\gathereditem{0}|. % % \DescribeMacro{\loopthroughitemswithcommand} % Once the items are gathered, it will probably be necessary to loop through % all of them. % Of course a package author can do so manually, but |getitems| provides a % built-in way to do so by calling |\loopthroughitemswithcommand|\marg{macro}. % The \meta{macro} must be a control sequence taking exactly one argument; % it will be called successively with the item text. % For example, % \begin{quote} % \begin{tabular}{p{3in}p{1.5in}} % \begin{minipage}{3in} % \begin{Verbatim}[gobble=6] % \gatheritems{% % Zero % \item One % \item Two % \item Three % } % \loopthroughitemswithcommand{\fbox} % \end{Verbatim} % \end{minipage} % & % \begin{minipage}{1.5in} % \gatheritems{% % Zero % \item One % \item Two % \item Three % } % \loopthroughitemswithcommand{\fbox} % \end{minipage} % \end{tabular} % \end{quote} % The result is the same as processing |\fbox{One}|, then |\fbox{Two}|, and finally |\fbox{Three}|. % Note that |\loopthroughitemswithcommand| deliberately ignores the zeroth entry, which occurs % before the first |\item|. % % \DescribeMacro{currentitemnumber} % Typically the package author will create a custom macro to process each item. % This macro may make use of the index of the loop, which is stored in the % \LaTeX\ counter |currentitemnumber|. % \DescribeMacro{\ifgatherbeginningofloop} % A conditional |\ifgatherbeginningofloop| is also available, % which only evaluates as true when % processing the first item; it is thus functionally equivalent to % |\ifnum1=\c@currentitemnumber|. % The custom macro may take advantage of this to run special code for the first item only. % % % \section{Example} % An example using |getitems| to create a custom environment may be informative. % We use the |\NewEnviron| command from the |environ| package % (automatically loaded by |getitems|) to define a |question| environment; % the body between the |\begin{question}| and |\end{question}| is available as |\BODY|. % \medskip % % \par \noindent % \begin{tabular}{p{3in}p{1.5in}} % \begin{minipage}{3in} % \small % \begin{Verbatim}[gobble=6] % \def\doitem#1{\item #1\hfill $\Box$}% % \NewEnviron{question}{% % \expandafter\gatheritems\expandafter{\BODY}% % \gathereditem{0}% % \begin{itemize} % \loopthroughitemswithcommand{\doitem} % \end{itemize} % } % \begin{question} % Who proved the unsolvability of the quintic? % \item Abel % \item Galois % \item Lie % \end{question} % \end{Verbatim} % \end{minipage} % & % \begin{minipage}{1.5in} % \def\doitem#1{\item #1\hfill $\Box$}% % \NewEnviron{question}{% % \expandafter\gatheritems\expandafter{\BODY}% % \gathereditem{0}% % \begin{itemize} % \loopthroughitemswithcommand{\doitem} % \end{itemize} % } % \begin{question} % Who proved the unsolvability of the quintic? % Check the appropriate box. % \item Abel % \item Galois % \item Lie % \end{question} % \end{minipage} % \end{tabular} % \medskip % % % This second example shows that nested environments are handled as expected. % \medskip % % \par \noindent % \begin{tabular}{p{3in}p{2in}} % \begin{minipage}{3in} % \small % \begin{Verbatim}[gobble=6] % \def\doitem#1{\item[$\Box$] % \fbox{\parbox[t]{1.75in}{#1}}}% % \NewEnviron{question}{% % \expandafter\gatheritems\expandafter{\BODY}% % \gathereditem{0}% % \begin{itemize} % \loopthroughitemswithcommand{\doitem} % \end{itemize} % } % \begin{question} % Who proved the unsolvability of the quintic? % Check the appropriate box. % \item Abel % \begin{itemize} % \item Born August 5, 1802 % \item Died April 6, 1829 % \end{itemize} % \item Galois % \begin{itemize} % \item Born October 25, 1811 % \item Died May 31, 1832 % \end{itemize} % \item Lie % \begin{itemize} % \item Born December 17, 1842 % \item Died February 18, 1899 % \end{itemize} % \end{question} % \end{Verbatim} % \end{minipage} % & % \begin{minipage}{2.5in} % \def\doitem#1{\item[$\Box$] \fbox{\parbox[t]{1.75in}{#1}}}% % \NewEnviron{question}{% % \expandafter\gatheritems\expandafter{\BODY}% % \gathereditem{0}% % \begin{itemize} % \loopthroughitemswithcommand{\doitem} % \end{itemize} % } % \begin{question} % Who proved the unsolvability of the quintic? % Check the appropriate box. % \item Abel % \begin{itemize} % \item Born August 5, 1802 % \item Died April 6, 1829 % \end{itemize} % \item Galois % \begin{itemize} % \item Born October 25, 1811 % \item Died May 31, 1832 % \end{itemize} % \item Lie % \begin{itemize} % \item Born December 17, 1842 % \item Died February 18, 1899 % \end{itemize} % \end{question} % \end{minipage} % \end{tabular} % % % \StopEventually{} % % \section{Implementation} % % We need the |trimspaces| package to remove excess spaces % from the items we find. % Although the |environ| package is not used by |getitems| itself, % it will almost certainly be needed. % \begin{macrocode} \RequirePackage{environ} \RequirePackage{trimspaces} \let\xa=\expandafter % \end{macrocode} % \begin{macro}{\gathereditem} % The $k$th item found will be stored in the macro |\getitems@item@|\meta{k}; % the user can access it through the |\gathereditem| macro. % \begin{macrocode} \def\gathereditem#1{\csname getitems@item@#1\endcsname} % \end{macrocode} % \end{macro} % % \begin{macro}{numgathereditems} % We define the \LaTeX\ counter |numgathereditems|. % \begin{macrocode} \newcounter{numgathereditems} % \end{macrocode} % \end{macro} % % \begin{macro}{\gatheritems} % The main control sequence of this package is |\gatheritems|. % The na\"ive strategy is to use the delimiter mechanism of \TeX\ to % split the text at the first occurrence of the token ``|\item|.'' % We add |\getitems@relax| before, % and ``|\item\getitems@terminalitem|'' after, the text to help us % detect empty items and prevent % errors after we have found all the genuine |\item|'s. % \begin{macrocode} \long\def\gatheritems#1{% \setcounter{getitems@begindepth}{0}% \setcounter{numgathereditems}{0}% \xa\long\xa\gdef\csname getitems@item@0\endcsname{}% \gatheritems@int\getitems@relax#1\item\getitems@terminalitem\getitems@endgatheritems \xa\let\xa\gatheredheader\xa=\csname getitems@item@0\endcsname } % \end{macrocode} % \end{macro} % The trouble with the na\"ive strategy is that it won't handle nested % environments correctly. To do that, we need to keep track of how deeply % nested we are with the macro |\getitems@trackbegindepth|, defined below. % That macro stores its results in the \LaTeX\ counter |getitems@begindepth|; % a value of 0 indicates the top-level within the argument of |\gatheritems|. % \begin{macrocode} \def\@getitems@terminalitem{\getitems@terminalitem}% \def\@dummy@relax{\getitems@relax}% \long\def\gatheritems@int#1\item#2\getitems@endgatheritems{% \getitems@trackbegindepth{#1}% \ifnum\c@getitems@begindepth=0\relax % \end{macrocode} % At this point we have gathered a complete |\item|; we have not stopped % accidentally at a sub|\item|. The original |\item| might have had no content, % in which case |#1| will be simply ``|\getitems@relax|'', and we do nothing; % otherwise we strip off the |\getitems@relax| and store those tokens in % |\getitems@item@|\meta{numgathereditems}. % \begin{macrocode} \def\getitems@test@i{#1}% \ifx\getitems@test@i\@dummy@relax \relax \else \xa\xa\xa\g@addto@macro \xa\xa\csname getitems@item@\the\c@numgathereditems\endcsname \xa{\getitems@stripfirsttokenfrom#1\getitems@endstrip}% \fi % \end{macrocode} % Now we test whether we have reached the end of the text to be parsed. % This is the case if |#2| is simply |\getitems@terminalitem|, % and we stop the recursion. % Otherwise there is at least one more |\item| to process, so we increment |numgathereditems|, % prepare |\getitems@item@|\meta{k+1}, % and prepare to recurse. % \begin{macrocode} \def\getitems@test@ii{#2}% \ifx\getitems@test@ii\@getitems@terminalitem \let\getitems@next=\relax \else \stepcounter{numgathereditems}% \xa\gdef\csname getitems@item@\the\c@numgathereditems\endcsname{}% \def\getitems@next{\gatheritems@int\getitems@relax#2\getitems@endgatheritems}% \fi \else % \end{macrocode} % We are now in the case where |getitems@begindepth|${}\neq 0$. % This essentially means that the text in |#1| has more |\begin|'s than |\end|'s, % so we have not read a complete |\item|; we stopped at an ``|\item|'' token % within a sub-environment. % We save the text gathered so far to |\getitems@item@|\meta{k}, % including the |\item| we parsed by mistake, % and then call |\gatheritems@int| again to sweep up more tokens. % \begin{macrocode} \xa\xa\xa\g@addto@macro \xa\xa\csname getitems@item@\the\c@numgathereditems\endcsname \xa{\getitems@stripfirsttokenfrom#1\getitems@endstrip}% \xa\g@addto@macro\csname getitems@item@\the\c@numgathereditems\endcsname{\item}% \def\getitems@next{\gatheritems@int\getitems@relax#2\getitems@endgatheritems}% \fi \getitems@next } % \end{macrocode} % This next macro is used by |\gatheritems@int| to strip off a % dummy |\getitems@relax| token from the beginning of its first parameter. % \begin{macrocode} \long\def\getitems@stripfirsttokenfrom#1#2\getitems@endstrip{#2} % \end{macrocode} % Here is the code used to track the depth of nesting of |\begin|'s in a text. % \begin{macrocode} \newcounter{getitems@begindepth} \long\def\getitems@trackbegindepth#1{% \getitems@trackbegindepth@int#1\getitems@terminalbegindepth\getitems@endtrackbegindepth } \def\@getitems@begin{\begin}% \def\@getitems@end{\end}% \def\@getitems@terminalbegindepth{\getitems@terminalbegindepth}% \long\def\getitems@trackbegindepth@int#1#2\getitems@endtrackbegindepth{% \def\getitems@test@i{#1}% \ifx\getitems@test@i\@getitems@begin \advance\c@getitems@begindepth by 1\relax \else \ifx\getitems@test@i\@getitems@end \advance\c@getitems@begindepth by -1\relax \fi \fi \def\getitems@test@ii{#2}% \trim@spaces@in\getitems@test@ii \ifx\getitems@test@ii\@getitems@terminalbegindepth \let\getitems@trackbegindepth@next=\relax \else \def\getitems@trackbegindepth@next{% \getitems@trackbegindepth@int#2\getitems@endtrackbegindepth}% \fi \getitems@trackbegindepth@next } % \end{macrocode} % \begin{macro}{\loopthroughitemswithcommand} % Finally, we define the user-level command to loop through % the gathered items from 1 through |numgathereditems|. % \begin{macrocode} \newif\ifgatherbeginningofloop \newcounter{currentitemnumber} \def\loopthroughitemswithcommand#1{% \setcounter{currentitemnumber}{1}% \gatherbeginningoflooptrue \loopthroughitemswithcommand@int{#1}% } \def\loopthroughitemswithcommand@int#1{% \ifnum\c@currentitemnumber>\c@numgathereditems\relax \let\getitems@loop@next=\relax% \else \xa\xa\xa#1\xa\xa\xa{\csname getitems@item@\the\c@currentitemnumber\endcsname}% \def\getitems@loop@next{\loopthroughitemswithcommand@int{#1}}% \stepcounter{currentitemnumber}% \fi \gatherbeginningofloopfalse \getitems@loop@next } % \end{macrocode} % \end{macro} % % \Finale \endinput