3

I'm creating a style file for deliverables for a project. During the draft stage, deliverables need to have a revision history in a table (version, date, modifications, modified by).

In the style file, I define a command:

\newcommand{\makedeliverable}{ % very long template
}

In any deliverable made using the template, \newdeliverable \makedeliverable is used immediately after \begin{document} to create the cover page, table of contents, list of figures etc. (I appreciate this might not be the best way, but it works well and I was able to produce it quickly).

I'd also like this command to create the revision history table, based on revisions specified by a user in their own preamble.

So in the .sty file (as part of the \makedeliverable command} I would have:

\begin{tabular}{|p{0.2\textwidth} | p{0.3\textwidth} | p{0.3\textwidth} | p{0.2\textwidth} |}
    \hline
   \textbf{Version} & \textbf{Date} & \textbf{Modifications} &     \textbf{Modified by}\\
\hline
% something here to populate the table
\end{tabular}

And also a command definition along the lines of:

\newcommand{\addrevision}[4]{ %something
}

Which would be called in the preamble with (e.g.):

\addrevision{0.1, 2017-11-27, Added new stuff, J. Bloggs}
\addrevision{0.2, 2017-11-27, Added more new stuff, B. Smith}

With my programming hat on, I'd have \addrevision populate an array which is then printed out in the table. But I'm unsure how to go about this in LaTeX.

Any advice will be welcome.

6

The code below does what I think you want. It uses LaTeX3 to build up a token list for the revision history. The code assumes that the \addrevision{...} lines are added in "revision order". Following the recommendations in the booktabs manual, I have dropped the vertical rules as the general opinion is that tables look better without them (and it is trivial to add them back if they are required).

The code defines two commands:

  • \addrevision{...}: this accepts/expects a comma separated list of the four pieces of "revision data": version, date, modifications, modifier
  • \RevisionHistory: inserts the current revision history

Here is the output:

enter image description here

...and here is the code:

\documentclass{article}
\usepackage{expl3}
\usepackage{booktabs}

\ExplSyntaxOn
\tl_new:N \l_revisions_tl% to hold revision history
\newcommand\addrevision[1]{% add to \l_revisions_tl
  \regex_split:nnN {\,} {#1} \l_tmpa_seq% split #1 on commas
  \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl% don't want & before first entry
  \tl_put_right:NV \l_revisions_tl \l_tmpa_tl
  \seq_map_inline:Nn \l_tmpa_seq { \tl_put_right:Nn \l_revisions_tl {&##1} }
  \tl_put_right:Nn \l_revisions_tl {\\}% add \\ for end of line
}
\newcommand\RevisionHistory{%
  \begin{tabular}{p{0.2\textwidth}p{0.3\textwidth}p{0.3\textwidth}p{0.2\textwidth}}
     \toprule
     \textbf{Version} & \textbf{Date} & \textbf{Modifications} & \textbf{Modified~by}\\
     \midrule
     \tl_use:N \l_revisions_tl
     \bottomrule
  \end{tabular}
}
\ExplSyntaxOff

\begin{document}

  \addrevision{0.1, 2017-11-27, Added new stuff, J. Bloggs}
  \addrevision{0.2, 2017-11-27, Added more new stuff, B. Smith}

  \RevisionHistory

\end{document}

I have used the same columns widths as in the OP but, probably, columns 1,2 and 4 should all be narrowed and the modifications column should be wider. If the revision history goes on for pages then something like longtable or supertabular should be used.

EDIT

Here is a second version using expl3 sequences. This is closer in spirit to the approach suggested in the OP and the code is shorter and, arguably, easier to understand.

\documentclass{article}
\usepackage{expl3}
\usepackage{booktabs}

\ExplSyntaxOn
\seq_new:N \l_revisions_seq
\newcommand\addrevision[1]{% add to \l_revisions_seq
  \seq_put_right:Nn \l_revisions_seq {#1}
}
\newcommand\RevisionHistory{%
\begin{tabular}{p{0.2\textwidth}p{0.3\textwidth}p{0.3\textwidth}p{0.2\textwidth}}
     \toprule
     \textbf{Version} & \textbf{Date} & \textbf{Modifications} & \textbf{Modified~by}\\
     \midrule
     \seq_map_inline:Nn \l_revisions_seq {% loop over revisions
       \regex_split:nnN {\,} {##1} \l_tmpa_seq% split on commas
       \seq_use:Nn \l_tmpa_seq {&}% print the table row
       \\
     }
     \\[-12pt]% \\ is necessary here but it inserts a blank line, so hack...
     \bottomrule
  \end{tabular}
}
\ExplSyntaxOff

\begin{document}

  \addrevision{0.1, 2017-11-27, Added new stuff, J. Bloggs}
  \addrevision{0.2, 2017-11-27, Added more new stuff, B. Smith}

  \RevisionHistory

\end{document}

For reasons I don't understand, I have had to use the hack \\[-12pt]\bottomrule to prevent a blank line from appearing at the bottom of the table. The output is the same as before

| improve this answer | |
  • +1, but you \usepackage{xparse} and code \newcommand? – TeXnician Nov 27 '17 at 15:51
  • No, that's not what I meant, I just thought that including it without actually using it is a bit pointless, but your code is alright. Maybe the l3 guys would point out that this would be one more step towards the separation of code and document level, but I'm not really sure, whether this is necessary here. – TeXnician Nov 27 '17 at 16:52
  • 1
    No, LaTeX3 is loaded by expl3, which is loaded by xparse. xparse itself only provides the layers like \NewDocumentCommand etc. on top. – TeXnician Nov 27 '17 at 20:23
  • The first method given in this solution worked perfectly! Thanks very much. – ThetaSigma Nov 28 '17 at 13:00
5

The mandatory \xintFor solution.

\documentclass{article}
\usepackage{xinttools}
\usepackage{booktabs}

\newcommand\Revisions {}%

\newcommand\addrevision[1]{% 
% append a new revision, the csv list being converted to braced items
    \global\oodef\Revisions
% oodef = twice expand contents, second expansion will expand \Revisions
% itself; first expansion propagates to \xintcsvtolist to convert #1
% into braced items, which we will use with \xintListWithSep later on
    {\expandafter\Revisions\expandafter{\romannumeral0\xintcsvtolist{#1}}}%
}

\newcommand\RevisionHistory{%
  \begin{tabular}{p{0.2\textwidth}p{0.3\textwidth}p{0.3\textwidth}p{0.2\textwidth}}
     \toprule
     \textbf{Version} & \textbf{Date} & \textbf{Modifications} & \textbf{Modified~by}\\
     \midrule
% we need to double the # here because we are inside a macro definition
     \xintFor* ##1 in \Revisions
     \do
         {\xintListWithSep{&}{##1}\\}% one row for each revision
     \bottomrule
  \end{tabular}
}

\begin{document}

  \addrevision{0.1, 2017-11-27, Added new stuff, J. Bloggs}
  \addrevision{0.2, 2017-11-27, Added more new stuff, B. Smith}

  \RevisionHistory

\end{document}

enter image description here

| improve this answer | |
  • \xintListWithSep{&}{##1} is a bit overkill: ##1 is simply for example {0.1}{2017-11-27}{...}{...} and we only need a macro \newcommand\mymacro[4]{#1&#2&#3&#4} in this specific case and usage will be \mymacro##1 (no braces... we need \mymacro to see its four arguments...). – user4686 Nov 28 '17 at 9:49
4

You can let \addrevision write the table rows to a temporary file that you \input in \makedeliverable.

\documentclass{article}
\usepackage{booktabs}
%%%%% In sty-file
\newwrite\RevisionFile
\immediate\openout\RevisionFile=\jobname.Revision.txt
\newcommand\addrevision[4]{%
  \immediate\write\RevisionFile{#1 & #2 & #3 & #4 \noexpand\\}  
}
\newcommand\makedeliverable{%
  \immediate\closeout\RevisionFile
  \begin{tabular}{p{0.2\textwidth}p{0.3\textwidth}p{0.3\textwidth}p{0.2\textwidth}}
    \toprule
    \textbf{Version} & \textbf{Date} & \textbf{Modifications} &     \textbf{Modified by}\\\midrule
    \input{\jobname.Revision.txt}
    \bottomrule
  \end{tabular}
}
%%%%% End of sty-file
%%%%% Revisions
\addrevision{0.1}{2017-11-27}{Added new stuff}{J. Bloggs}
\addrevision{0.2}{2017-11-27}{Added more new stuff}{B. Smith}
%%%%%
\begin{document}
\makedeliverable

\end{document}

The file <filename>.Revision.txtis created as

0.1 & 2017-11-27 & Added new stuff & J. Bloggs \\
0.2 & 2017-11-27 & Added more new stuff & B. Smith \\

And the output:

enter image description here

| improve this answer | |

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.