From 1990ab7568a3a134d271c1450640e42bd5c2b839 Mon Sep 17 00:00:00 2001 From: Frank Mittelbach Date: Mon, 20 Jan 2025 18:27:11 +0100 Subject: [PATCH] Set template keys docu (#1614) * typo * reordered and changed docu * testing for empty in \SetTemplateKays; more docu; changelog; open: csname change * update to \SetKnownTemplateKeys et al; new handling of user supplied keys (checking) --- WIP * missing decl * Make a proper error message and document why \SetTemplateKeys may not be a good choice compared to \SetKnownTemplateKeys * update after review * updating and reorganizing docu after review * typo --- base/changes.txt | 6 + base/lttemplates.dtx | 170 +++++++++++++----- required/latex-lab/latex-lab-block.dtx | 110 ++++++++++-- .../testfiles-block/blocks-user-keys-01.lvt | 39 ++++ .../testfiles-block/blocks-user-keys-01.tlg | 92 ++++++++++ required/latex-lab/update-blocks-all.sh | 1 + 6 files changed, 352 insertions(+), 66 deletions(-) create mode 100644 required/latex-lab/testfiles-block/blocks-user-keys-01.lvt create mode 100644 required/latex-lab/testfiles-block/blocks-user-keys-01.tlg diff --git a/base/changes.txt b/base/changes.txt index db1c4a407..75cd02ec7 100644 --- a/base/changes.txt +++ b/base/changes.txt @@ -10,6 +10,12 @@ not part of the distribution. * ltkeys.dtx Parse global options only once per package (gh/1619) +2025-01-13 Frank Mittelbach + + * lttemplates.dtx (subsection{User functions}): + Speed up common case of \SetTemplateKeys, i.e., when the + key/val list is empty. + 2025-01-11 Udi Fogiel * ltluatex.dtx (subsubsection{Handlers}): diff --git a/base/lttemplates.dtx b/base/lttemplates.dtx index a6d6b1fd2..a090d2a31 100644 --- a/base/lttemplates.dtx +++ b/base/lttemplates.dtx @@ -36,7 +36,7 @@ %<*driver> % \fi \ProvidesFile{lttemplates.dtx} - [2024-11-17 v1.0d LaTeX Kernel (Prototype document functions)] + [2025-01-20 v1.0e LaTeX Kernel (Prototype document functions)] % \iffalse \documentclass{l3doc} \GetFileInfo{lttemplates.dtx} @@ -91,7 +91,7 @@ % \item Loading one of the many packages to customise certain elements of % the standard classes. % \item Loading a completely different document class, such as -% \textsf{KOMA-Script} or \textsf{memoir}, that allows easy customisation. +% \textsf{KOMA-Script} or \textsf{memoir}, that allows easy customization. % \end{itemize} % All three of these approaches have their drawbacks and learning curves. % @@ -99,7 +99,7 @@ % introduced at the beginning of this section, so that document authors who % are not programmers can easily change the design of their documents. % \pkg{lttemplates} also makes it easier for \LaTeX{} programmers to provide -% their own customisations on top of a pre-existing class. +% their own customizations on top of a pre-existing class. % % \section{What is a document?} % @@ -120,7 +120,7 @@ % contain the same sort of information but present it in slightly % different ways. For example, the difference between a numbered and an % unnumbered section, \cs{section} and |\section*|, or the difference -% between an itemised list or an enumerated list. +% between an itemized list or an enumerated list. % % There are three distinct layers in the definition of % \enquote{a document} at this level @@ -180,7 +180,7 @@ % \section{Templates} % \label{sec:templates} % -% A \emph{template} is a generalised design solution for representing +% A \emph{template} is a generalized design solution for representing % the information of a specified type. Templates that do the same % thing, but in different ways, are grouped together by their type % and given separate names. There are two important parts to a template: @@ -320,7 +320,7 @@ % \toprule % \multicolumn{1}{l}{Key-type} & Description of binding \\ % \midrule -% \ & Boolean variable, \emph{e.g}.~\cs{l_tmpa_bool} \\ +% boolean & Boolean variable, \emph{e.g}.~\cs{l_tmpa_bool} \\ % choice % & List of choice implementations % (see Section~\ref{sec:choices-key}) \\ @@ -351,12 +351,95 @@ % In the final argument of \cs{DeclareTemplateCode} the assignment of % keys defined by the template may be delayed by including the command % \cs{AssignTemplateKeys}. If this is \emph{not} present, keys are assigned -% immediately before the template code. If \cs{AssignTemplateKeys} is +% immediately before the template code. If an +% \cs{AssignTemplateKeys} command is % present, assignment is delayed until this point. Note that the % command must be \emph{directly} present in the code, not placed % within a nested command/macro. % \end{function} % + +% \begin{function}{\SetKnownTemplateKeys,\SetTemplateKeys,\UnusedTemplateKeys} +% \begin{syntax} +% \cs{SetKnownTemplateKeys} \Arg{type} \Arg{template} \Arg{keyvals} +% \cs{SetTemplateKeys} \Arg{type} \Arg{template} \Arg{keyvals} +% +% \cs{UnusedTemplateKeys} \% all \meta{keyvals} unused by previous \cs{SetKnownTemplateKeys} +% \end{syntax} +% +% In the final argument of \cs{DeclareTemplateCode} one can also +% overwrite (some of) the current template key value settings by +% using the command \cs{SetKnownTemplateKeys} or +% \cs{SetTemplateKeys}, i.e., they can overwrite the template default values and +% the values assigned by the instance. +% +% The \cs{SetKnownTemplateKeys} and \cs{SetTemplateKeys} commands +% are only supported within the code of a template; using them +% elsewhere has unpredictable results. If they are used together +% with \cs{AssignTemplateKeys} then the latter command should come first +% in the template code. +% +% The main use case for these commands is the situation where there +% is an argument (normally \texttt{\#1}) to the template in which +% a key/value list can be specified that overwrites the normal +% settings. In that case one could use +%\begin{quote} +% \verb/\SetKnownTemplateKeys/\Arg{type}\Arg{template}\verb/{#1}/ +%\end{quote} +% to process this key/value list inside the template. +% +% If \cs{SetKnownTemplateKeys} is executed and the \meta{keyvals} argument contains keys not known to the +% \meta{template} they are simply ignored and stored in the +% tokenlist \cs{UnusedTemplateKeys} +% without generating an error. This way it is possible to +% apply the same key/val list specified by the user on a +% document-level command or environment to several templates, which +% is useful, if the command or environment is implemented by calling several different template instances. +% +% \end{function} +% +% As a variation of that, you can use this key/val +% list the first time, and for the next template instance use what remains in +% \cs{UnusedTemplateKeys} (i.e., the key/val list with only the keys that have not been processed previously). The final processing step could then be +% \cs{SetTemplateKeys}, which unconditionally attempts to set the +% \meta{keyvals} received in its third argument. This command +% complains if any of them are unknown keys. Alternatively, you +% could use \cs{SetKnownTemplateKeys} and afterwards check whether \cs{UnusedTemplateKeys} is empty.\footnote{Using +% \cs{SetTemplateKeys} exposes the inner structure of the template +% keys when generating an error. This is something one may want to +% avoid as it can be confusing to the user, especially if several +% templates are involved. In that +% case use \cs{SetKnownTemplateKeys} and afterwards check whether +% \cs{UnusedTemplateKeys} is empty; if it is not empty then generate your own +% error message.} +% +% For example, a list, such as \env{enumerate}, is made up from a +% \texttt{blockenv}, \texttt{block}, \texttt{list}, and a +% \texttt{para} template and in the single user-supplied optional +% argument of \env{enumerate} key/values for any of these templates might +% be specified. +% +% In fact, in the particular example of list environments, +% the supplied key/value list is also saved and then applied to each +% \cs{item} which is implemented through an \texttt{item} +% template. This way, one can specify one-off settings for all the items +% of a single list (on the environment level), as well as to +% individual items within that list (by specifying them in the +% optional argument of an \cs{item}). With +% \cs{SetKnownTemplateKeys} and \cs{SetTemplateKeys} working +% together, it is possible to provide this flexibility and still +% alert the user when one of their keys is misspelled. +% +% On the other hand you may want to allow for +% \enquote{misspellings} without generating an error or a +% warning. For example, if you define a template that accepts only a +% few keys, you might just want to ignore anything specified in the +% source when you use this template in place of a different one, +% without the need to alter the document source. Or you might just +% generate a warning message, which is easy, given that the unused +% key/values are available in the \cs{UnusedTemplateKeys} variable. +% +% % \begin{function}{\DeclareTemplateCopy} % \begin{syntax} % \cs{DeclareTemplateCopy} @@ -418,7 +501,7 @@ % For keys which accept the values \texttt{true} and \texttt{false} % both the boolean and choice key types can be used. As template % interfaces are intended to prompt clarity at the design level, the -% boolean key type should be favoured, with the choice type reserved +% boolean key type should be favored, with the choice type reserved % for keys which take arbitrary values. % % \section{Instances} @@ -432,10 +515,10 @@ % are input into it. % % For example, a template might say \enquote{here is a section with or -% without a number that might be centred or left aligned and print its +% without a number that might be centered or left aligned and print its % contents in a certain font of a certain size, with a bit of a gap % before and after it} whereas an instance declares \enquote{this is a -% section with a number, which is centred and set in $12\,\text{pt}$ +% section with a number, which is centered and set in $12\,\text{pt}$ % italic with a $10\,\text{pt}$ skip before and a % $12\,\text{pt}$ skip after it}. Therefore, an instance is just a % frozen version of a template with specific settings as chosen by the @@ -516,8 +599,8 @@ % detailed by the \meta{type} declaration. This in effect % is the same as creating an instance using \cs{DeclareInstance} % and immediately using it with \cs{UseInstance}, but without the -% instance having any further existence. It is therefore useful where -% a template needs to be used once. +% instance having any further existence. This command is therefore useful when +% a template needs to be used only once. % % This function can also be used as the argument to \texttt{instance} % key types: @@ -565,39 +648,6 @@ % template untouched. % \end{function} % -% \section{\emph{Ad hoc} adjustment of templates} -% -% \begin{function}{\SetTemplateKeys} -% \begin{syntax} -% \cs{SetTemplateKeys} \Arg{type} \Arg{template} \Arg{keyvals} -% \end{syntax} -% At point of use it may be useful to apply changed to individual instances. -% This is supported as each template key is made available for adjustment -% using \cs{SetTemplateKeys}. -% \end{function} -% -% For example, after -% \begin{verbatim} -% \NewTypeType{MyObj}{0} -% \DeclareTemplateInterface{MyObj}{TemplateA}{0} -% { -% akey: tokenlist , -% bkey: function{2} -% } -% \DeclareTemplateCode{MyObj}{TemplateA}{0} -% { -% akey = SomeTokens , -% bkey = \func:nn , -% } -% \end{verbatim} -% the template keys could be adjusted in an \emph{ad hoc} fashion using -% \begin{verbatim} -% \SetTemplateKeys{MyObj}{TemplateA} -% { -% akey = OtherTokens , -% bkey = \AltFunc:nn -% } -% \end{verbatim} % % \section{Getting information about templates and instances} % @@ -2619,11 +2669,35 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\SetTemplateKeys} -% A friendly wrapper +% \begin{macro}{\SetKnownTemplateKeys,\SetTemplateKeys} +% A friendly wrapper, with some speed up for the common case of the +% third argument being empty. +% \changes{2025-01-08}{v1.0d}{Test for empty key/val list to speed up processing} +% \begin{macrocode} +\cs_new_protected:Npn \SetKnownTemplateKeys #1#2#3 + { + \tl_if_empty:oTF {#3} + { + \tl_set_eq:NN \UnusedTemplateKeys \c_empty_tl + } + { + \keys_set_known:noN { template / #1 / #2 } {#3} \UnusedTemplateKeys + } + } +% \end{macrocode} +% % \begin{macrocode} \cs_new_protected:Npn \SetTemplateKeys #1#2#3 - { \keys_set_known:nnN { template / #1 / #2 } {#3} \l_@@_tmp_clist } + { + \tl_if_empty:oF {#3} + { + \keys_set:no { template / #1 / #2 } {#3} + } + } +% \end{macrocode} +% +% \begin{macrocode} +\tl_new:N \UnusedTemplateKeys % \end{macrocode} % \end{macro} % diff --git a/required/latex-lab/latex-lab-block.dtx b/required/latex-lab/latex-lab-block.dtx index b0f8315f6..618b4821c 100644 --- a/required/latex-lab/latex-lab-block.dtx +++ b/required/latex-lab/latex-lab-block.dtx @@ -9,8 +9,8 @@ % % https://www.latex-project.org/lppl.txt % -\def\ltlabblockdate{2025-01-06} -\def\ltlabblockversion{0.8x} +\def\ltlabblockdate{2025-01-18} +\def\ltlabblockversion{0.8z} %<*driver> \documentclass[kernel]{l3doc} \usepackage{amstext} @@ -235,7 +235,7 @@ % \begin{TemplateDescription}{blockenv}{display} % % \TemplateKey{env-name}{tokenlist} -% {Name of the environment used only in tracing}{} +% {Name of the environment used in tracing and error messages}{} % \TemplateKey{tag-name}{tokenlist} % {Name of the tag in the PDF. If not explicitly given % the name is defined by the \key{tagging-recipe}}{} @@ -1109,8 +1109,7 @@ { \@@_debug_typeout:n{\l_@@_env_name_tl -env-start} % - \tl_if_empty:nF {#1} { \SetTemplateKeys{blockenv}{display}{#1} } -% + \SetKnownTemplateKeys{blockenv}{display}{#1} % \end{macrocode} % We need to know later if we have nested blockenvs inside % a flattened environment. Whenever we start a new blockenv we @@ -1201,7 +1200,16 @@ \UseInstance{block} { \l_@@_block_instance_tl - \int_use:N \g_block_nesting_depth_int } - {#1} + \UnusedTemplateKeys +% \end{macrocode} +% After this instance has been processed, any remaining unused keys +% are stored in \cs{UnusedTemplateKeys} and we can make use of this +% data later as +% long as we do not call another instance that also does unused key +% processing and overwrites it. This is what happens below, so we better save its +% current value for now. +% \begin{macrocode} + \tl_set_eq:NN \l_@@_unused_blockenv_keys_tl \UnusedTemplateKeys % \end{macrocode} % After the block instance call the para and then inner (list) % instance if either or both are @@ -1233,7 +1241,28 @@ % not clean use "o"? { - \int_use:N \l_@@_inner_level_counter_tl } } - {#1} + \l_@@_unused_blockenv_keys_tl +% \end{macrocode} +% Again the instance may has processed a few keys from the sofar +% unused keys, so we update \cs{l_@@_unused_blockenv_keys_tl} to +% match the new reality. +% \begin{macrocode} + \tl_set_eq:NN \l_@@_unused_blockenv_keys_tl \UnusedTemplateKeys + } +% \end{macrocode} +% At this point, the \cs{l_@@_unused_blockenv_keys_tl} token list +% should either be empty or it should contain only keys that are suitable for +% the item template, but right now there is no code to test that +% can test +% the latter; it would help probably if we have an interface for +% this. +% +% For now we handle that when the first item is encountered, but +% that isn't realy clean.\fmi{fix} +% \begin{macrocode} + \tl_if_empty:NF \l_@@_unused_blockenv_keys_tl + { + % check if only item template keys remain } % \end{macrocode} % We finish off with \cs{l_@@_final_code_tl} which defaults to @@ -1246,6 +1275,14 @@ % \end{template} % % +% \begin{macro}{\l_@@_unused_blockenv_keys_tl} +% +% \begin{macrocode} +\tl_new:N \l_@@_unused_blockenv_keys_tl +% \end{macrocode} +% \end{macro} +% +% % \begin{macro}{\l__tag_block_flattened_level_int} % Count the levels of nested blockenvs starting with the first that % is \enquote{flattened}. The counter is defined in lttagging.dtx, @@ -1443,7 +1480,7 @@ para-class = \l__tag_para_attr_class_tl , } { - \tl_if_empty:nF {#1} { \SetTemplateKeys{para}{std}{#1} } + \SetTemplateKeys{para}{std}{#1} \skip_set:Nn \@rightskip \rightskip } % \end{macrocode} @@ -1485,7 +1522,7 @@ parindent = \l_@@_parindent_dim , } { - \tl_if_empty:nF {#1} { \SetTemplateKeys{block}{display}{#1} } + \SetKnownTemplateKeys{block}{display}{#1} % \end{macrocode} % \fmi{generalize heading usage (or drop?)} % \begin{macrocode} @@ -1695,7 +1732,7 @@ % if they should alter all items the right place would be the list % environment. For this reason we need to store the values and then % set them inside the \tn{item} template code using -% \tn{SetTemplateKeys} in the appropriate context (template type +% \tn{SetKnownTemplateKeys} in the appropriate context (template type % and template name). This is done in % \cs{@@_evaluate_saved_user_keys:nn}. The context is provided in % the two arguments (because different list environments may use @@ -1714,10 +1751,8 @@ %\cs_new:Npn \@@_save_user_keys:n #1 { % \tl_if_empty:nTF {#1} % { \cs_set_eq:NN \@@_evaluate_saved_user_keys:nn \use_none:nn } -% { -% \cs_set:Npe \@@_evaluate_saved_user_keys:nn ##1##2 -% { \SetTemplateKeys{##1}{##2}{ \exp_not:n{#1} } } -% } +% { \cs_set:Npe \@@_evaluate_saved_user_keys:nn ##1##2 +% { \SetKnownTemplateKeys{##1}{##2}{ \exp_not:n{#1} } } %} % \end{macrocode} % \end{macro} @@ -1756,10 +1791,10 @@ % \changes{v0.8s}{2024/10/03}{Prepare \cs{@@_evaluate_saved_user_keys:nn} % for use in \tn{item}} % \begin{macrocode} - \tl_if_empty:nTF {#1} + \tl_if_empty:oTF {#1} { \cs_set_eq:NN \@@_evaluate_saved_user_keys:nn \use_none:nn } { - \SetTemplateKeys{list}{std}{#1} + \SetKnownTemplateKeys{list}{std}{#1} % \end{macrocode} % The setup for \cs{@@_evaluate_saved_user_keys:nn} is a bit trcky % and has to be done with \cs{cs_set:Npe} even though we don't want @@ -1778,7 +1813,21 @@ % resulting in very odd and puzzling behavior. % \begin{macrocode} \cs_set:Npe \@@_evaluate_saved_user_keys:nn ##1##2 - { \SetTemplateKeys{##1}{##2}{ \exp_not:n{ #1 } } } + { \SetKnownTemplateKeys{##1}{##2}{ + \exp_not:o { \UnusedTemplateKeys } + } +% \end{macrocode} +% +% \begin{macrocode} + \exp_not:n { + \tl_if_empty:NF \UnusedTemplateKeys + { + \msg_error:nnee { block } { unknown-keys } + { \l_@@_env_name_tl \space environment} + \UnusedTemplateKeys + } + } + } } % \end{macrocode} % Has this list a counter name defined in the instance? @@ -1861,6 +1910,20 @@ \@@_debug_typeout:n{template:list:std~end} } % \end{macrocode} +% +% The message that is used above when we are left with keys that +% are unknown: +% \begin{macrocode} +\msg_new:nnnn { block } { unknown-keys } + { Some~ keys~ specified~ on~ the~ #1~ are~ unknown. } + { + The~ following~ keys~ are~ unknown~ and~ their~ + values~ are~ ignored:\\ + \space\space #2\\ + Perhaps~ a~ misspelling~ or~ the~ current~ template~ + instance~ uses~ special~ keys. + } +% \end{macrocode} % % % Extra keys to support enumitem conventions: @@ -1954,7 +2017,18 @@ % to make use of user keys on the list level} % \begin{macrocode} \@@_evaluate_saved_user_keys:nn {item}{std} - \tl_if_empty:nF{#1}{ \SetTemplateKeys{item}{std}{#1} } +% \end{macrocode} +% We don't care whether all of the user keys from the list level have +% been applied, but those explicitly set on the \cs{item} command +% should be aplicable, so we generate an error if that isn't the case: + % \begin{macrocode} + \SetKnownTemplateKeys{item}{std}{#1} + \tl_if_empty:NF \UnusedTemplateKeys + { + \msg_error:nnee { block } { unknown-keys } + { \noexpand\item command } + \UnusedTemplateKeys + } % \end{macrocode} % % If no optional argument was given then \cs{l_@@_label_given_tl} diff --git a/required/latex-lab/testfiles-block/blocks-user-keys-01.lvt b/required/latex-lab/testfiles-block/blocks-user-keys-01.lvt new file mode 100644 index 000000000..1bb1e10c3 --- /dev/null +++ b/required/latex-lab/testfiles-block/blocks-user-keys-01.lvt @@ -0,0 +1,39 @@ + +\DocumentMetadata{ + testphase=latest + ,uncompress + ,pdfversion=2.0 + ,debug={ +% para, +% log=vv + } +} + +\DebugBlocksOn + + +\documentclass{article} + +\input{regression-test} + +\usepackage{kantlipsum} + +\newtheorem{theorem}{Theorem} + +\begin{document} + +\START + + \begin{enumerate}[label-format=\fbox{#1},blub] + \item abc + \item[beng=bang] def + \begin{itemize}[foo=bar,leftmargin=3cm] + \item abc + \item[a=a,b=b] ghi + + xyz + \end{itemize} + \end{enumerate} + + +\end{document} diff --git a/required/latex-lab/testfiles-block/blocks-user-keys-01.tlg b/required/latex-lab/testfiles-block/blocks-user-keys-01.tlg new file mode 100644 index 000000000..9ce73df0e --- /dev/null +++ b/required/latex-lab/testfiles-block/blocks-user-keys-01.tlg @@ -0,0 +1,92 @@ +This is a generated file for the l3build validation system. +Don't change this file in any respect. +==> enumerate-env-start +==> use instance: list-1 +==> @endpe=false on input line ... +==> use instance: enum-1 +==> template:list:std +==> template:list:std end +==> template:item:std +! Package block Error: Some keys specified on the enumerate environment are unknown. +For immediate help type H . + ... +l. ... \item a + bc +The following keys are unknown and their values are ignored: + blub +Perhaps a misspelling or the current template instance uses special keys. +==> item everypar on input line ... +==> increment P on input line ... +==> template:item:std +! Package block Error: Some keys specified on the enumerate environment are unknown. +For immediate help type H . + ... +l. ... \item[beng=bang] + def +The following keys are unknown and their values are ignored: + blub +Perhaps a misspelling or the current template instance uses special keys. +! Package block Error: Some keys specified on the \item command are unknown. +For immediate help type H . + ... +l. ... \item[beng=bang] + def +The following keys are unknown and their values are ignored: + beng={bang} +Perhaps a misspelling or the current template instance uses special keys. +==> item everypar on input line ... +==> increment P on input line ... +==> itemize-env-start +==> use instance: list-2 +==> increment /P on input line ... +==> use instance: itemize-1 +==> template:list:std +==> template:list:std end +==> template:item:std +! Package block Error: Some keys specified on the itemize environment are unknown. +For immediate help type H . + ... +l. ... \item a + bc +The following keys are unknown and their values are ignored: + foo={bar} +Perhaps a misspelling or the current template instance uses special keys. +==> item everypar on input line ... +==> increment P on input line ... +==> template:item:std +! Package block Error: Some keys specified on the itemize environment are unknown. +For immediate help type H . + ... +l. ... \item[a=a,b=b] + ghi +The following keys are unknown and their values are ignored: + foo={bar} +Perhaps a misspelling or the current template instance uses special keys. +! Package block Error: Some keys specified on the \item command are unknown. +For immediate help type H . + ... +l. ... \item[a=a,b=b] + ghi +The following keys are unknown and their values are ignored: + a={a},b={b} +Perhaps a misspelling or the current template instance uses special keys. +==> item everypar on input line ... +==> increment P on input line ... +==> blockenv common ending on input line ... +==> blockenv common ending on input line ... +==> Structure-end P at list-end on input line ... +==> flattened=false on input line ... +==> Structure-end text-unit after displayblock on input line ... +[1 +] (blocks-user-keys-01.aux) +Package tagpdf Info: Finalizing the tagging structure: +(tagpdf) Writing out ~29 structure objects +(tagpdf) with ~11 'MC' leaf nodes. +(tagpdf) Be patient if there are lots of objects! +Package tagpdf Info: writing ParentTree +Package tagpdf Info: writing IDTree +Package tagpdf Info: writing RoleMap +Package tagpdf Info: writing ClassMap +Package tagpdf Info: writing NameSpaces +Package tagpdf Info: writing StructElems +Package tagpdf Info: writing Root diff --git a/required/latex-lab/update-blocks-all.sh b/required/latex-lab/update-blocks-all.sh index 7d35a7a72..a08eecc52 100644 --- a/required/latex-lab/update-blocks-all.sh +++ b/required/latex-lab/update-blocks-all.sh @@ -2,6 +2,7 @@ l3build save -cconfig-block -epdftex \ + blocks-user-keys-01 \ tagging-0767