%! Class = packdoc %! Author = Jander Moreira %! Date = 2024/10 \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{packdoc}[2025/01/31 v0.1 A tool to document LaTeX packages] \NewDocumentCommand{\PDVersion}{}{v0.1} \NewDocumentCommand{\PDDate}{}{2025/01/31} \RequirePackage{pgfopts} \RequirePackage{etoolbox} \newbool{packdoc@UsePresets} \pgfkeys{ packdoc/.cd, presets/.is if = packdoc@UsePresets, } \ProcessPgfOptions{/packdoc} \appto{\ttfamily}{\frenchspacing}{}{} \appto{\tableofcontents}{\bigskip} \RequirePackage{snaptodo} \RequirePackage{marginnote} \setlength{\marginparwidth}{3cm} \RequirePackage{enumitem} \setlist{nosep} \RequirePackage{textcomp} \RequirePackage[all]{nowidow} \RequirePackage{multicol} \RequirePackage{ragged2e} \RequirePackage{geometry} \geometry{top = 2.5cm, bottom = 2cm, right = 2.5cm, left = 4cm} \RequirePackage{hyperref} \hypersetup{ colorlinks, urlcolor = blue!20!black, linkcolor = blue!10!black, citecolor = black!80, } \RequirePackage{makeidx} \makeindex \RequirePackage{minted} \RequirePackage{tcolorbox} \tcbuselibrary{most, minted} \newbool{packdoc@InExample} \newlength{\packdoc@ColorBoxIndent} \setlength{\packdoc@ColorBoxIndent}{3em} \tcbset{ description/.style = { coltitle = black, fontupper = \normalsize, colbacktitle = black, titlerule = 0.001pt, enhanced jigsaw, breakable, sharp corners, flush right, top = 0.5ex, bottom = 0pt, left = \packdoc@ColorBoxIndent, right = 0pt, opacitybacktitle = 0.05, opacityframe = 0, opacityback = 0, }, example/.style = { enhanced jigsaw, breakable, colback = blue!3, colframe = blue!40!black!30, sharp corners, box align = top, boxrule = 1pt, fontupper = \footnotesize, fontlower = \footnotesize, listing engine = minted, minted options = { fontsize = \footnotesize, breaklines, autogobble, }, before lower = {\booltrue{packdoc@InExample}}, after lower = {\boolfalse{packdoc@InExample}}, } } \newtcblisting{PDListing}{listing options = {language = latex}, example, listing only} \newtcblisting{PDExample}{example} \newmintinline{latex}{} \cslet{PDInline}{\latexinline} \RequirePackage{cleveref} % Support code \ExplSyntaxOn \seq_new:N \g_packdoc_list_of_elements_seq \cs_new:Npn \packdoc_put_element:n #1 { \seq_put_left:Nn \g_packdoc_list_of_elements_seq { #1 } } \cs_new:Npn \packdoc_if_element_exists:nTF #1#2#3 { \seq_if_in:NnTF \g_packdoc_list_of_elements_seq { #1 } { #2 } { #3 } } \NewDocumentCommand{\packdoc@PutElement}{ m }{ \packdoc_put_element:n { #1 } } \NewDocumentCommand{\packdoc@IfElementExists}{ m +m +m }{ \packdoc_if_element_exists:nTF { #1 } { #2 } { #3 } } \ExplSyntaxOff \packdoc@PutElement{packdoc@Element} \newlength{\packdoc@ContentLength} \NewDocumentCommand{\packdoc@IfHasLength}{ m +m +m }{% \settowidth{\packdoc@ContentLength}{#1}% \ifdimgreater{\packdoc@ContentLength}{0pt}{#2}{#3}% } \NewDocumentCommand{\PackageName}{ O{} m }{% \begingroup% \PDSet{#1}% \mbox{\packdoc@PackageStyle#2}% \endgroup% } \NewDocumentCommand{\FileName}{ m }{% \mbox{\textsf{#1}}% } % Arguments \NewDocumentCommand{\Argument}{ O{} m }{% \begingroup% \PDSet{#1}% \textcolor{packdoc@ArgumentColor}{$\langle$\normalfont\small\textsl{#2}$\rangle$}% \endgroup% } \NewDocumentCommand{\MArg}{ O{} m }{% \mbox{\texttt{\{}\Argument[#1]{#2}\texttt{\}}}% } \NewDocumentCommand{\OArg}{ O{} m }{% \mbox{\texttt{[}\Argument[#1]{#2}\texttt{]}}% } \NewDocumentCommand{\AArg}{ O{} m }{% \mbox{\texttt{<}\Argument[#1]{#2}\texttt{>}}% } \NewDocumentCommand{\PArg}{ m }{% \mbox{\texttt{\{#1\}}}% } % General text %\NewDocumentCommand{\Deprecated}{}{\textcolor{red!80!black}{(deprecated)}} %\NewDocumentCommand{\Empty}{}{% % \mbox{\normalfont\textcolor{black!60}{\textsl{--empty--}}} %} \NewDocumentCommand{\PDTilde}{}{\raisebox{-0.6ex}{\~{}}} % Elements \pgfkeys{ /packdoc/.cd, argument color/.code = {\colorlet{packdoc@ArgumentColor}{#1}}, package style/.store in = \packdoc@PackageStyle, % element specific options prefix/.code = {\csdef{packdoc@\csuse{packdoc@Element}@Prefix}{#1}}, prefix/.value required, arguments prefix/.code = {\csdef{packdoc@\csuse{packdoc@Element}@ArgumentsPrefix}{#1}}, arguments prefix/.value required, complement prefix/.code = {\csdef{packdoc@\csuse{packdoc@Element}@ComplementPrefix}{#1}}, complement prefix/.value required, index heading/.code = {\csdef{packdoc@\csuse{packdoc@Element}@IndexEntry}{#1}}, index heading/.value required, index remark/.code = {\csdef{packdoc@\csuse{packdoc@Element}@IndexRemark}{#1}}, index remark/.value required, font/.code = {\csdef{packdoc@\csuse{packdoc@Element}@Font}{#1}}, font/.value required, color/.code = {\colorlet{packdoc@\csuse{packdoc@Element}@Color}{#1}}, color/.value required, no single index/.default = true, no single index/.code = {% \providebool{packdoc@\csuse{packdoc@Element}@NoSingleEntry}% \packdoc@CheckTrueFalse{no single index}{#1}{% \csuse{bool#1}{packdoc@\csuse{packdoc@Element}@NoSingleEntry}% }% }, no group index/.default = true, no group index/.code = {% \providebool{packdoc@\csuse{packdoc@Element}@NoGroupEntry}% \packdoc@CheckTrueFalse{no group index}{#1}{% \csuse{bool#1}{packdoc@\csuse{packdoc@Element}@NoGroupEntry}% } }, % version changes version prefix/.store in = \packdoc@VersionPrefix, header style/.store in = \packdoc@VersionStyle, entry style/.store in = \packdoc@ChangeStyle, } \NewDocumentCommand{\packdoc@CheckTrueFalse}{ m m m }{ \ifstrequal{#2}{true}{#3}{% \ifstrequal{#2}{false}{#3}{% \PackageError{packdoc}{Option '#1' expects 'true' or 'false'}% }% } } \NewDocumentCommand{\packdoc@SetElementDefault}{ m }{% \csdef{packdoc@Element}{#1}% current element \pgfkeys{ /packdoc/.cd, prefix = \csuse{packdoc@Element@Defaults@Prefix}, arguments prefix = \csuse{packdoc@Element@Defaults@ArgumentsPrefix}, complement prefix = \csuse{packdoc@Element@Defaults@ComplementPrefix}, index heading = \packdoc@Element, index remark = ~(\packdoc@Element), font = \csuse{packdoc@Element@Defaults@Font}, color = packdoc@Element@Defaults@Color, no single index = false, no group index = false, }% \csdef{packdoc@Element}{}% reset } \NewDocumentCommand{\PDSetElement}{ m >{ \TrimSpaces } m }{% \packdoc@IfElementExists{#1}{% \csdef{packdoc@Element}{#1}% current element \pgfkeys{/packdoc/.cd, #2}% \csdef{packdoc@Element}{}% reset }{% \PackageError{packdoc}{Element '#1' does not exist}% } } \NewDocumentCommand{\PDSet}{ >{ \TrimSpaces } m }{% \csdef{packdoc@Element}{Element@Defaults}% \pgfkeys{/packdoc/.cd, #1}% \csdef{packdoc@Element}{}% reset } \PDSet{ package style = \sffamily, argument color = orange!50!black, prefix = {}, arguments prefix = {}, complement prefix = \hfill, index heading = {}, index remark = {}, font = \ttfamily, color = .!75, no single index = false, no group index = false, } \ExplSyntaxOn \NewDocumentCommand{\PDNewElement}{ m m }{ \packdoc@IfElementExists{#1}{ \PackageError{packdoc}{Element~'#1'~already~created} }{ \packdoc@PutElement{#1} \packdoc@SetElementDefault{#1}% \PDSetElement{#1}{#2}% \exp_args:Nc \NewDocumentCommand { #1 }{ m }{ \PDElement{#1}{##1} } \exp_args:Nc \NewDocumentCommand { #1Def }{ m }{ \PDDefElement{#1}{##1} } \exp_args:Nc \NewDocumentCommand { #1Ref }{ m }{ \PDRefElement{#1}{##1} } \exp_args:Nc \NewDocumentCommand { #1Ind }{ m }{ \PDIndElement{#1}{##1} } \exp_args:Nc \NewDocumentCommand { #1Index }{ m }{ \PDIndexElement{#1}{##1} } \exp_args:Nc \NewDocumentCommand { #1RefInd }{ m }{ \PDRefIndElement{#1}{##1} } \NewDocumentEnvironment{#1*}{ m m m }{% \begin{element*}{#1}{##1}{##2}{##3} }{ \end{element*} } \NewDocumentEnvironment{#1def}{ m m m }{ \begin{elementdef}{#1}{##1}{##2}{##3} }{ \end{elementdef} } \NewDocumentEnvironment{#1env*}{ m m m }{ \begin{elementenv*}{#1}{##1}{##2}{##3} }{ \end{elementenv*} } \NewDocumentEnvironment{#1env}{ m m m }{ \begin{elementenv}{#1}{##1}{##2}{##3} }{ \end{elementenv} } } } \ExplSyntaxOff % #1: Element type % #2: Element instance \NewDocumentCommand{\PDElement}{ m m }{% \ifcsdef{\string\color@packdoc@#1@Color}{% \mbox{\textcolor{packdoc@#1@Color}{\csuse{packdoc@#1@Font}\csuse{packdoc@#1@Prefix}#2}}% }{% \mbox{\csuse{packdoc@#1@Prefix}\csuse{packdoc@#1@Font}#2}% }% } % #1: Element type % #2: Element instance \NewDocumentCommand{\PDIndexElement}{ m m }{% \ifbool{packdoc@#1@NoSingleEntry}{}{% \index{#2@\PDElement{#1}{#2}\csuse{packdoc@#1@IndexRemark}}% }% \ifbool{packdoc@#1@NoGroupEntry}{}{% \index{\csuse{packdoc@#1@IndexEntry}!#2@\PDElement{#1}{#2}}% }% } % #1: Element type % #2: Element instance \NewDocumentCommand{\PDIndElement}{ m m }{% \PDIndexElement{#1}{#2}% \PDElement{#1}{#2}% } % #1: Element type % #2: Element instance \NewDocumentCommand{\PDRefElement}{ m m }{% \hyperref[#1:#2]{\PDElement{#1}{#2}}% } % #1: Element type % #2: Element instance \NewDocumentCommand{\PDDefElement}{ m m }{% \phantomsection% \label{#1:#2}% \PDIndexElement{#1}{#2}% \PDElement{#1}{#2}% } % #1: Element type % #2: Element instance \NewDocumentCommand{\PDRefIndElement}{ m m }{% \PDIndexElement{#1}{#2}% \hyperref[#1:#2]{\PDElement{#1}{#2}}% } % #1: Element type % #2: Element instance % #3: Arguments % #4: Value \NewDocumentEnvironment{element*}{ m m m m }{% \begin{tcolorbox}[ description, title = {% \hspace{-\packdoc@ColorBoxIndent}% \PDElement{#1}{#2}% \packdoc@IfHasLength{#3}{\csuse{packdoc@#1@ArgumentsPrefix}#3}{}% \packdoc@IfHasLength{#4}{\csuse{packdoc@#1@ComplementPrefix}#4}{}% }, ]% }{% \end{tcolorbox}% \medskip% } % #1: Element type % #2: Element instance % #3: Arguments % #4: Default \NewDocumentEnvironment{elementdef}{ m m m m }{ \begin{element*}{#1}{#2}{#3}{#4} \PDIndexElement{#1}{#2}% \label{#1:#2} }{ \end{element*} } % #1: Element type % #2: Environment name % #3: Arguments % #4: Environment contents \NewDocumentEnvironment{elementenv*}{ m m m m }{% \begin{tcolorbox}% [ title = {% %! parser=off \hspace{-\packdoc@ColorBoxIndent}\latexinline!\begin!\texttt{\{}\csuse{#1}{#2}\texttt{\}}#3\par \hspace{-0.5\packdoc@ColorBoxIndent}% \packdoc@IfHasLength{#4}{#4}{\Argument{environment contents}}\par \hspace{-\packdoc@ColorBoxIndent}\latexinline!\end!\texttt{\{}\csuse{#1}{#2}\texttt{\}}% %! parser=on }, description, ] }{% \end{tcolorbox}% \medskip% } \NewDocumentEnvironment{elementenv}{ m m m m }{% \begin{elementenv*}{#1}{#2}{#3}{#4} \PDIndexElement{#1}{#2}% \label{#1:#2} }{% \end{elementenv*} } \ifbool{packdoc@UsePresets}{ \definecolor{packdoc@OptionColor}{HTML}{687821} \colorlet{packdoc@EnvironmentColor}{brown!80!magenta} \colorlet{packdoc@MacroColor}{green!50!black} \PDNewElement{Option}{ color = packdoc@OptionColor, arguments prefix = \texttt{ = }, index heading = Options, index remark = ~(option), } \PDNewElement{Macro}{ prefix = \textbackslash, color = packdoc@MacroColor, index heading = Macros, index remark = ~(macro), } \PDNewElement{Environment}{ color = packdoc@EnvironmentColor, index heading = Environments, index remark = ~(environment), } }{} %%%%%%%%%%%%%%%%%% \PDSet{ version prefix = {}, header style = \bfseries\footnotesize, entry style = \footnotesize\RaggedRight, } %% Internal commands \ExplSyntaxOn % StepChangeCounter: proceeds to a new change \int_zero_new:N \g_packdoc_change_counter_int \NewDocumentCommand{\packdoc@StepChangeCounter}{}{ \int_gadd:Nn \g_packdoc_change_counter_int { 1 } } \NewDocumentCommand{\packdoc@CurrentChangeCounter}{}{ \int_use:N \g_packdoc_change_counter_int } % SetChangeAttribute: sets the an attribute of the current change record % #1: attribute % #2: value \cs_new:Npn \set_change_attribute:nn #1#2 { \tl_clear_new:c { g_vc_change_ \int_use:N \g_packdoc_change_counter_int _#1_tl } \tl_gset:cn { g_vc_change_ \int_use:N \g_packdoc_change_counter_int _#1_tl } { #2 } } \NewDocumentCommand{\packdoc@SetChangeAttribute}{ m m }{ \set_change_attribute:nn { #1 } { #2 } } % SetChangeAttributeExpanded: sets the an attribute of the current change record % #1: attribute % #2: value \NewDocumentCommand{\packdoc@SetChangeAttributeExpanded}{ m m }{ \exp_args:Nnf \set_change_attribute:nn { #1 } { #2 } } % GetChangeInfo: returns the field of a change % #1: number of the change % #2: field % #3: (optional) sets macro instead of returning value \NewDocumentCommand{\packdoc@GetChangeInfo}{ m m o }{ \IfValueTF{#3}{ \tl_set:Nx #3 { \tl_use:c { g_vc_change_#1_#2_tl } } }{ \tl_use:c { g_vc_change_#1_#2_tl } } } % SetMacroChangeInfo: sets a macro with the field of a change % #1: macro % #2: number of the change % #3: field \NewDocumentCommand{\packdoc@SetMacroChangeInfo}{ m m m }{ \tl_clear_new:N #1 \exp_args:NNc \tl_set:NV #1 { g_vc_change_#2_#3_tl } } % RunChangesList: apply a macro to each change of a version % #1: version % #2: macro with a single mandatory argument \NewDocumentCommand{\packdoc@RunChangesList}{ m m }{ \seq_map_inline:cn { g_version_#1_seq } { #2 { ##1 } } } % packdoc@IfVersionExists % #1: version % #2: code if exists % #3: code if not exists \cs_new:Npn \packdoc_if_version_exists:nTF #1#2#3 { \seq_if_in:NnTF \g_packdoc_versions_list_seq { #1 } { #2 } { #3 } } \NewDocumentCommand{\packdoc@IfVersionExists}{ m +m +m }{ \packdoc_if_version_exists:nTF { #1 } { #2 } { #3 } } % PDNewVersion: add a version to the list os versions if it doesn't exist % #1: version % #2: date \seq_new:N \g_packdoc_versions_list_seq \cs_new:Npn \register_version:nn #1#2 { \seq_put_right:Nn \g_packdoc_versions_list_seq { #1 } \tl_clear_new:c { g_vc_version_#1_date_tl } \tl_gset:cn { g_vc_version_#1_date_tl } { #2 } \seq_new:c { g_version_#1_seq } } \NewDocumentCommand{\PDNewVersion}{ m m }{ \packdoc@IfVersionExists{#1}{ \PackageError{packdoc}{Version~'#1'~already~exists} }{ \register_version:nn { #1 } { #2 } } } % VersionDate: returns the date of a version OR sets a macro % with its (expanded) value % #1: version % #2: (optional) macro to store the value \NewDocumentCommand{\packdoc@VersionDate}{ m o }{ \IfValueTF{#2}{ \tl_set:Nx #2 { \tl_use:c { g_vc_version_#1_date_tl } } }{ \tl_use:c { g_vc_version_#1_date_tl } } } % AddChangeToVersion: add a change reference to the list of the version % (a new list will be created if necessary) % #1: version \cs_new:Npn \add_change_to_version:n #1 { % Add a reference (change number) to the list % \tl_clear_new:N \l_change_number_tl % \exp_args:NNe \tl_set:Nn \l_change_number_tl { \int_use:N \g_packdoc_change_counter_int } \exp_args:Nco \seq_gput_right:Nn { g_version_#1_seq } { \int_use:N \g_packdoc_change_counter_int } } \NewDocumentCommand{\packdoc@AddChangeToVersion}{ }{ \add_change_to_version:n { \tl_use:c { g_vc_change_ \int_use:N \g_packdoc_change_counter_int _version_tl } } } % RunVersionList: apply a macro to each version of the list % #1: macro with a single mandatory argument \NewDocumentCommand{\packdoc@RunVersionList}{ m }{ \seq_map_inline:Nn \g_packdoc_versions_list_seq { #1 { ##1 } } } \ExplSyntaxOff %% \newbool{packdoc@HideBox} \newbool{packdoc@NoListing} \pgfkeys{ /packdoc/.cd, version/.code = {\packdoc@SetChangeAttribute{version}{#1}}, title/.code = {\csdef{packdoc@BoxTitle}{#1}}, description/.code = {\packdoc@SetChangeAttribute{description}{#1}}, page/.code = {\packdoc@SetChangeAttributeExpanded{page}{#1}}, no page/.style = {page = {}}, no box/.is if = packdoc@HideBox, no listing/.is if = packdoc@NoListing, type/.is choice, type/new/.code = {\packdoc@SetChangeAttribute{type}{New in}}, type/update/.code = {\packdoc@SetChangeAttribute{type}{Updated in}}, type/change/.code = {\packdoc@SetChangeAttribute{type}{Changed in}}, type/removal/.code = {\packdoc@SetChangeAttribute{type}{Removed in}}, type/deprecation/.code = {\packdoc@SetChangeAttribute{type}{Deprecated in}}, .unknown/.code = {% \csedef{packdoc@local@Option}{type = \pgfkeyscurrentname}% \pgfkeysalsofrom{\packdoc@local@Option}% }, } \PDSet{ no box = false, no listing = false, } %% #1: (optional) todonotes options %% #2: text %\NewDocumentCommand{\packdoc@MarginNote}{ O{} > { \TrimSpaces } m }{% % \todo[bordercolor = blue!20, backgroundcolor = blue!10, % linecolor = blue!20, tickmarkheight = 0.2ex, size = \tiny, % noline, #1]{% % #2 % }% %} % PDAddChange: records a new change \NewDocumentCommand{\PDAddChange}{ m > { \TrimSpaces } m O{} }{% \packdoc@IfVersionExists{#1}{% \begingroup% \pgfkeys{ /packdoc/.cd, page = \thepage, version = #1, type = new, title = {}, description = {% \textbf{???}% \PackageWarning{packdoc}{A change without a description and without the 'no listing' option has been created.}% }, #2, }% \hspace{0pt}% \packdoc@AddChangeToVersion% \packdoc@SetChangeAttributeExpanded{star}{\ifbool{packdoc@NoListing}{*}{}}% \packdoc@SetChangeAttributeExpanded{label}{\packdoc@CurrentChangeCounter}% \packdoc@GetChangeInfo{\packdoc@CurrentChangeCounter}{label}[\packdoc@InfoResult]% \expandafter\label\expandafter{\packdoc@InfoResult:change}% \ifbool{packdoc@HideBox}{}{% %\reversemarginpar% %\ifbool{packdoc@InExample}{\let\marginpar\marginnote}{}% %\tikzset{ % notestyleraw/.append style = {rounded corners = 0pt, inner sep = 2pt}, %}% \snaptodo[ block sep = -0.2ex, call chain/.style = {draw = none}, margin block/.style = {font = \scriptsize, blue!75!black}, chain bias = -99in, % force to left margin #3 ]{% \packdoc@IfHasLength{\csuse{packdoc@BoxTitle}}{% \textbf{\csuse{packdoc@BoxTitle}}:\\% }{}% \sffamily% \packdoc@GetChangeInfo{\packdoc@CurrentChangeCounter}{type} \packdoc@VersionPrefix\packdoc@GetChangeInfo{\packdoc@CurrentChangeCounter}{version}% }% }% \endgroup% \packdoc@StepChangeCounter% }{% \PackageWarning{packdoc}{#1 is not a valid version. Ignored}% }% } %% % Internal commands \ExplSyntaxOn % Reading from file \ior_new:N \g_packdoc_input_io % LoadFile: reads a previous compiled version changes \cs_new:Nn \load_file: { \tl_clear_new:N \g_packdoc_file_contents_tl \ior_open:NnTF \g_packdoc_input_io { \jobname.vcind } { \ior_map_inline:Nn \g_packdoc_input_io { \tl_gput_right:Nn \g_packdoc_file_contents_tl { ##1 } } }{} } \NewDocumentCommand{\packdoc@LoadChanges}{}{ \load_file: } \NewDocumentCommand{\packdoc@FileContentsNotEmpty}{ +m }{ \tl_if_empty:NTF \g_packdoc_file_contents_tl {} { #1 } } % File contents \NewDocumentCommand{\packdoc@FileContents}{}{ \tl_use:N \g_packdoc_file_contents_tl } % Writing to file \iow_new:N \g_packdoc_output_io \tl_new:N \g_packdoc_output_buffer_tl % OpenFile: open file for writing \NewDocumentCommand{\packdoc@OpenFile}{ }{ \iow_open:Nn \g_packdoc_output_io { \jobname.vcind } } % CloseFile: close file \NewDocumentCommand{\packdoc@CloseFile}{ }{ \iow_close:N \g_packdoc_output_io } % WriteBuffer: write to file \cs_generate_variant:Nn \iow_now:Nn { NV } \NewDocumentCommand{\packdoc@WriteBuffer}{ }{ \iow_now:NV \g_packdoc_output_io \g_packdoc_output_buffer_tl \tl_clear:N \g_packdoc_output_buffer_tl } \NewDocumentCommand{\packdoc@AddToBuffer}{ +m }{ \tl_gput_right:Nn \g_packdoc_output_buffer_tl { #1 } } \cs_generate_variant:Nn \tl_gput_right:Nn { Nx } \NewDocumentCommand{\packdoc@AddCharToBuffer}{ m }{ \tl_gput_right:Nx \g_packdoc_output_buffer_tl { \iow_char:N #1 } } \ExplSyntaxOff % WriteVersionChange: write to file a single change % #1: change number \NewDocumentCommand{\packdoc@WriteVersionChange}{ m }{% \packdoc@AddToBuffer{ \PDPrintChange}% \packdoc@SetMacroChangeInfo{\packdoc@InfoResult}{#1}{star}% \expandafter\packdoc@AddToBuffer\expandafter{\packdoc@InfoResult}% % version number \packdoc@AddCharToBuffer{\{}% \packdoc@SetMacroChangeInfo{\packdoc@InfoResult}{#1}{description}% \expandafter\packdoc@AddToBuffer\expandafter{\packdoc@InfoResult}% \packdoc@AddCharToBuffer{\}}% % date \packdoc@AddCharToBuffer{\{}% \packdoc@SetMacroChangeInfo{\packdoc@InfoResult}{#1}{page}% \expandafter\packdoc@AddToBuffer\expandafter{\packdoc@InfoResult}% \packdoc@AddCharToBuffer{\}}% % label \packdoc@AddCharToBuffer{\{}% \packdoc@SetMacroChangeInfo{\packdoc@InfoResult}{#1}{label}% \expandafter\packdoc@AddToBuffer\expandafter{\packdoc@InfoResult}% \packdoc@AddCharToBuffer{\}}% \packdoc@WriteBuffer } \NewDocumentCommand{\packdoc@WriteVersion}{ m }{% %! parser = off \packdoc@AddToBuffer{\begin{vcversionitem}}% %! parser = on \packdoc@AddCharToBuffer{\{}% \packdoc@AddToBuffer{#1}% \packdoc@AddCharToBuffer{\}}% \packdoc@AddCharToBuffer{\{}% \packdoc@VersionDate{#1}[\packdoc@InfoResult]% \expandafter\packdoc@AddToBuffer\expandafter{\packdoc@InfoResult}% \packdoc@AddCharToBuffer{\}}% \packdoc@WriteBuffer \packdoc@RunChangesList{#1}{\packdoc@WriteVersionChange}% %! parser = off \packdoc@AddToBuffer{\end{vcversionitem}}% %! parser = on \packdoc@WriteBuffer } \NewDocumentCommand{\packdoc@SaveChanges}{}{% \packdoc@OpenFile% \packdoc@RunVersionList{\packdoc@WriteVersion}% \packdoc@WriteBuffer \packdoc@CloseFile% } \NewDocumentCommand{\PDPrintChanges}{ O{} }{% \packdoc@FileContentsNotEmpty{ \begingroup% \PDSet{#1} \section*{Change History} \begin{multicols}{2} \packdoc@FileContents% \end{multicols} \endgroup% } } %% Printing the list of changes \NewDocumentEnvironment{vcversionitem}{ m m }{% \par\noindent% {\packdoc@VersionStyle\packdoc@VersionPrefix#1 (#2)} \vspace*{0.2em}\par% \begingroup% \setlength{\tabcolsep}{0pt}% \packdoc@ChangeStyle% }{% \endgroup% \vspace{0.5em}% } \newlength{\packdoc@PageNumberWidth} \setlength{\packdoc@PageNumberWidth}{1cm} \NewDocumentCommand{\PDPrintChange}{ s m m m }{% \IfBooleanF{#1}{% \begingroup% \hspace{0.25cm}% \parbox[b]{\dimexpr \linewidth - \packdoc@PageNumberWidth - 0.25cm}{% \setlength{\hangindent}{0.7cm}#2% \ifstrempty{#3}{}{\dotfill}% }% \ifstrempty{#3}{}{% \hangindent=0.6\linewidth% \hspace{-0.05cm}\dotfill~\hspace{0.05cm}\hyperref[#4:change]{#3}% }% \par\vspace{0.5em} \endgroup% } } %% Version numbering \ExplSyntaxOn \tl_new:N \g_packdoc_prefix_text_tl \tl_new:N \g_packdoc_number_tl % todo: turn to int? \tl_new:N \g_packdoc_suffix_text_tl \regex_new:N \g_versioning_regex \regex_set:Nn \g_versioning_regex { [^\.]+ } \regex_new:N \g_subversioning_regex \regex_set:Nn \g_subversioning_regex { ([^\d]*)(\d+)(.*) } \cs_new:Npn \subversioning_check:n #1 { \seq_clear_new:N \l_subparts_seq \regex_extract_all:NnNTF \g_subversioning_regex { #1 } \l_subparts_seq { \tl_gset:Nx \g_packdoc_prefix_text_tl { \seq_item:Nn \l_subparts_seq {2} } \tl_gset:Nx \g_packdoc_number_tl { \seq_item:Nn \l_subparts_seq {3} } \tl_gset:Nx \g_packdoc_suffix_text_tl { \seq_item:Nn \l_subparts_seq {4} } }{ \tl_gset:Nx \g_packdoc_prefix_text_tl { #1 } \tl_gclear:N \g_packdoc_number_tl \tl_gclear:N \g_packdoc_suffix_text_tl } } \cs_new:Npn \versioning_check:n #1 { \seq_clear_new:N \l_parts_seq \regex_extract_all:NnN \g_versioning_regex { #1 } \l_parts_seq \seq_map_inline:Nn \l_parts_seq { \subversioning_check:n { ##1 } <<(\tl_use:N \g_packdoc_prefix_text_tl) (\tl_use:N \g_packdoc_number_tl) (\tl_use:N \g_packdoc_suffix_text_tl)>> } } \NewDocumentCommand{\PDVersioningCheck}{ m }{ \par\noindent[\texttt{#1}:~\versioning_check:n { #1 }]\bigskip\par } \ExplSyntaxOff % Hooks \AtBeginDocument{% \packdoc@LoadChanges% \sloppy% } \AtEndDocument{\packdoc@SaveChanges}