[latex3-commits] [git/LaTeX3-latex3-latex2e] develop: Add l3keys2e code as ltkeys (#710) (272f8e8d)
GitHub
noreply at github.com
Wed Jan 12 18:43:35 CET 2022
Repository : https://github.com/latex3/latex2e
On branch : develop
Link : https://github.com/latex3/latex2e/commit/272f8e8d07d38f5b8f92406e2050fcda4d993d1f
>---------------------------------------------------------------
commit 272f8e8d07d38f5b8f92406e2050fcda4d993d1f
Author: Joseph Wright <joseph.wright at morningstar2.co.uk>
Date: Wed Jan 12 17:43:35 2022 +0000
Add l3keys2e code as ltkeys (#710)
>---------------------------------------------------------------
272f8e8d07d38f5b8f92406e2050fcda4d993d1f
base/changes.txt | 10 +
base/doc/ltnews35.tex | 41 +++
base/doc/source2e.tex | 2 +
base/format.ins | 1 +
base/ltclass.dtx | 66 +++--
base/ltkeys.dtx | 454 +++++++++++++++++++++++++++++
base/testfiles-lthooks/ltcmdhooks-001.tlg | 2 +-
base/testfiles/github-0710.lvt | 50 ++++
base/testfiles/github-0710.tlg | 70 +++++
base/testfiles/tlb-rollback-005.luatex.tlg | 4 +-
base/testfiles/tlb-rollback-005.tlg | 4 +-
base/testfiles/tlb-rollback-005.xetex.tlg | 4 +-
12 files changed, 679 insertions(+), 29 deletions(-)
diff --git a/base/changes.txt b/base/changes.txt
index c8f78ffe..8ae1fd7b 100644
--- a/base/changes.txt
+++ b/base/changes.txt
@@ -84,6 +84,16 @@ are not part of the distribution.
* All *.dtx: Replaced \StopEventually by \MaybeStop
+2021-11-30 Joseph Wright <joseph.wright at latex-project.org>
+
+ * ltclasses.dtx
+ New option handling routine using ltkeys
+
+2021-11-26 Joseph Wright <joseph.wright at latex-project.org>
+
+ * ltkeys.dtx
+ New file to integrate keyval option processing into the kernel
+
2021-11-17 Marcel Krüger <Marcel.Krueger at latex-project.org>
* ltluatex.dtx:
diff --git a/base/doc/ltnews35.tex b/base/doc/ltnews35.tex
index 9b8d9c0f..31bce8a1 100644
--- a/base/doc/ltnews35.tex
+++ b/base/doc/ltnews35.tex
@@ -193,6 +193,47 @@ and also in the documentation of the \pkg{pdfmanagement-testphase} package.
\section{New or improved commands}
+\subsection{A kevyal approach to option handling}
+
+The classical \LaTeXe{} method for handling options, using \cs{ProcessOptions},
+treats each entry in the list as a string. Many package authors have sought to
+extend this handling by treating each entry as a key--value pair (keyval)
+instead. To-date, this has required the use of additional packages, for example
+\pkg{kvoptions}.
+
+The \LaTeX{} team have for some time offered the package \pkg{l3keys2e} to
+allow keyvals defined using the L3 programming layer module \pkg{l3keys} to act
+as package options. This ability has now been integrated directly into the
+kernel. As part of this integration, the syntax for processing keyval options
+has been refined, such that
+\begin{verbatim}
+\ProcessKeyOptions
+\end{verbatim}
+will now automatically pick up the package name as the key \emph{family},
+unless explicitly given as an optional argument.
+\begin{verbatim}
+\ProcessKeyOptions[family]
+\end{verbatim}
+A version which does not consider global options,
+\cs{ProcessKeyPackageOptions}, is also available.
+
+To support creating key options in for this mechanism, the new command
+\cs{DeclareKeys} has been added. This works using the same general
+approach as \pkg{l3keys} or \pkg{pgfkeys}: each key has one or more
+\emph{properties} which define its behavior.
+
+Options for packages which use this new approach will not be checked for
+clashes by the kernel. Instead, each time a \cs{usepackage} or
+\cs{RequirePackage} line is encountered, the list of options given will be
+passed to \cs{ProcessKeyPackageOptions}. Options which can only be given
+the first time a package is loaded can be marked using the property
+\texttt{.usage = load}, and will result in a warning if used in a subsequent
+package loading line.
+
+Package options defined in this way can also be set within a package using
+the new command \cs{SetKeys}, which again takes an optional argument
+to specify the \emph{family}, plus a mandatory one for the options themselves.
+
\subsection{Floating point and integer calculations}
The L3 programming layer offers expandable commands for calculating
diff --git a/base/doc/source2e.tex b/base/doc/source2e.tex
index 88252394..9eea9ddd 100644
--- a/base/doc/source2e.tex
+++ b/base/doc/source2e.tex
@@ -328,6 +328,8 @@ page_precedence "rnaA"
\DocInclude{ltclass} % Package & Class interface
+ \DocInclude{ltkeys} % Key-based option management (L3 module)
+
\DocInclude{ltfilehook} % Hook management for files (L3 module)
\DocInclude{ltshipout}% \shipout redefinition (L3 module)
diff --git a/base/format.ins b/base/format.ins
index ca16ee18..88d33d3c 100644
--- a/base/format.ins
+++ b/base/format.ins
@@ -204,6 +204,7 @@ the system are in the document `cfgguide.tex'.
\from{ltbibl.dtx}{2ekernel}
\from{ltpage.dtx}{2ekernel}
\from{ltclass.dtx}{2ekernel,tracerollback}
+ \from{ltkeys.dtx}{2ekernel} % L3 layer module
\from{ltfilehook.dtx}{2ekernel} % L3 layer module
\from{ltshipout.dtx}{2ekernel} % L3 layer module
\from{ltoutput.dtx}{2ekernel}
diff --git a/base/ltclass.dtx b/base/ltclass.dtx
index 573cd081..0ff3f711 100644
--- a/base/ltclass.dtx
+++ b/base/ltclass.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltclass.dtx}
- [2021/08/25 v1.4f LaTeX Kernel (Class & Package Interface)]
+ [2021/12/09 v1.5a LaTeX Kernel (Class & Package Interface)]
% \iffalse
\documentclass{ltxdoc}
\GetFileInfo{ltclass.dtx}
@@ -2175,27 +2175,21 @@
% \begin{macrocode}
\@ifl at aded\@currext\@currname
% \end{macrocode}
-% If the package is already loaded, check that there were no option
-% clashes:
-% \changes{v1.1b}{1998/05/07}
-% {Modify help message for latex/2805}
-% \begin{macrocode}
- {\@if at ptions\@currext{\@currname}{#2}{}%
- {\@latex at error
- {Option clash for \@cls at pkg\space \@currname}%
- {The package \@currname\space has already been loaded
- with options:\MessageBreak
- \space\space[\@ptionlist{\@currname.\@currext}]\MessageBreak
- There has now been an attempt to load it
- with options\MessageBreak
- \space\space[#2]\MessageBreak
- Adding the global options:\MessageBreak
- \space\space
- \@ptionlist{\@currname.\@currext},#2\MessageBreak
- to your \noexpand\documentclass declaration may fix this.%
- \MessageBreak
- Try typing \space <return> \space to proceed.}}%
- \@firstofone}%
+% In the current preferred approach, a key family name will exist for
+% processing using \pkg{ltkeys}. In that case, we replace the previous
+% package options with with the new ones, then call the key handler.
+% Otherwise, we use the more classical clash handler.
+% \begin{macrocode}
+ {%
+ \@ifundefined{opt at fam@\@currname.\@currext}
+ {\@onefilewithoptions at clashchk{#2}}
+ {%
+ \@namedef{opt@\@currname.\@currext}{#2}%
+ \expandafter\expandafter\expandafter\ProcessKeyPackageOptions
+ \expandafter\expandafter\expandafter
+ [\csname opt at fam@\@currname.\@currext\endcsname]%
+ }%
+ }%
{\makeatletter
% \end{macrocode}
% The next line seems to be necessary for 2.09 compatibility (the
@@ -2298,6 +2292,34 @@
\@reset at ptions}
% \end{macrocode}
%
+% \begin{macro}{\@onefilewithoptions at clashchk}
+% If the package is already loaded, check that there were no option
+% clashes.
+% \changes{v1.1b}{1998/05/07}
+% {Modify help message for latex/2805}
+% \changes{v1.5a}{2021/11/30}
+% {Separated out from \cs{@onefilewithoptions}}
+% \begin{macrocode}
+\def\@onefilewithoptions at clashchk#1{%
+ \@if at ptions\@currext{\@currname}{#1}{}%
+ {\@latex at error
+ {Option clash for \@cls at pkg\space \@currname}%
+ {The package \@currname\space has already been loaded
+ with options:\MessageBreak
+ \space\space[\@ptionlist{\@currname.\@currext}]\MessageBreak
+ There has now been an attempt to load it
+ with options\MessageBreak
+ \space\space[#1]\MessageBreak
+ Adding the global options:\MessageBreak
+ \space\space
+ \@ptionlist{\@currname.\@currext},#1\MessageBreak
+ to your \noexpand\documentclass declaration may fix this.%
+ \MessageBreak
+ Try typing \space <return> \space to proceed.}}%
+ \@firstofone}
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macrocode}
\let\@currpkg at reqd\@empty
% \end{macrocode}
diff --git a/base/ltkeys.dtx b/base/ltkeys.dtx
new file mode 100644
index 00000000..ef760c8a
--- /dev/null
+++ b/base/ltkeys.dtx
@@ -0,0 +1,454 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2021
+% The LaTeX Project and any individual authors listed elsewhere
+% in this file.
+%
+% This file is part of the LaTeX base system.
+% -------------------------------------------
+%
+% It may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3c
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% https://www.latex-project.org/lppl.txt
+% and version 1.3c or later is part of all distributions of LaTeX
+% version 2008 or later.
+%
+% This file has the LPPL maintenance status "maintained".
+%
+% The list of all files belonging to the LaTeX base distribution is
+% given in the file `manifest.txt'. See also `legal.txt' for additional
+% information.
+%
+% The list of derived (unpacked) files belonging to the distribution
+% and covered by LPPL is defined by the unpacking scripts (with
+% extension .ins) which are part of the distribution.
+%
+% \fi
+%
+% \iffalse
+%%% From File: ltkeys.dtx
+%
+%<*driver>
+% \fi
+\ProvidesFile{ltkeys.dtx}
+ [2021/11/30 v1.0a LaTeX Kernel (Kevyal options)]
+% \iffalse
+\documentclass{l3doc}
+\GetFileInfo{ltkeys.dtx}
+\title{\filename}
+\date{\filedate}
+\author{%
+ \LaTeX{} Team}
+
+\begin{document}
+ \maketitle
+ \DocInput{ltkeys.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \section{Creating and using keyval options}
+%
+% As with any key--value input, using key--value pairs as package or
+% class options has two parts: creating the key options and setting (using)
+% them. Options created in this way \emph{may} be used after package loading
+% as general key--value settings: this will depend on the nature of the
+% underlying code.
+%
+% \begin{function}{\DeclareKeys}
+% \begin{syntax}
+% \cs{DeclareKeys} \oarg{family} \marg{declarations}
+% \end{syntax}
+% Creates a series of options from a comma-separated \meta{declarations} list.
+% Each entry in this list is a key--value pair, with the \meta{key} having one
+% or more \meta{properties}. A small number of \enquote{basic}
+% \meta{properties} are described below. The full range of properties,
+% provided by \pkg{l3keys}, can also be used for more powerful processing.
+% See \texttt{interface3} for the full details.
+%
+% The basic properties provided here are
+% \begin{itemize}
+% \item \texttt{.if} --- sets a \TeX{} \cs{if...} switch
+% \item \texttt{.store} --- stores a value in a macro
+% \item \texttt{.usage} -- defines whether the option can be given only
+% when loading (\texttt{load}), in the preamble (\texttt{preamble}) or
+% has no limitation on scope (\texttt{general})
+% \end{itemize}
+% The part of the \meta{key} before the \meta{property} is the \meta{name},
+% with the \meta{value} working with the \meta{property} to define the
+% behaviour of the option.
+%
+% For example, with
+% \begin{verbatim}
+% \DeclareKeys[mypkg]
+% {
+% draft.if = @mypkg at draft ,
+% draft.usage = preamble ,
+% name.store = \@mypkg at name ,
+% name.usage = load ,
+% second-name.store = \@mypkg at other@name
+% }
+% \end{verbatim}
+% three options would be create. The option \texttt{draft} can be given
+% anywhere in the preamble, and will set a switch called \cs{if at mypkg@draft}.
+% The option \texttt{name} can only be given during package loading, and will
+% save whatever value it is given in \cs{@mypkg at name}. Finally, the option
+% \texttt{second-name} can be given anywhere, and will save its value in
+% \cs{@mypkg at other@name}.
+%
+% Keys created \emph{before} the use of
+% \cs{ProcessKeyOptions}/\cs{ProcessKeyPackageOptions} act as package options.
+% \end{function}
+%
+% \begin{function}{\ProcessKeyOptions}
+% \begin{syntax}
+% \cs{ProcessKeyOptions} \oarg{family}
+% \end{syntax}
+% The \cs{ProcessKeyOptions} function is used to check the current
+% option list against the keys defined for \meta{family}. Global (class)
+% options and local (package) options are checked when this function
+% is called in a package.
+% \end{function}
+%
+% \begin{function}{\ProcessKeyPackageOptions}
+% \begin{syntax}
+% \cs{ProcessKeyPackageOptions} \oarg{family}
+% \end{syntax}
+% This function works in a similar manner to \cs{ProcessKeyOptions}.
+% When used in a package, \cs{ProcessKeyPackageOptions}
+% will not examine any global (class) class options available. In contrast,
+% \cs{ProcessKeyOptions} does parse class options (in common with the
+% classical \cs{ProcessOptions} command).
+% \end{function}
+%
+% \begin{function}{\SetKeys}
+% \begin{syntax}
+% \cs{SetKeys} \oarg{family} \Arg{keyvals}
+% \end{syntax}
+% Sets (applies) the explicit list of \meta{keyvals} for the \meta{family}:
+% it the latter is not given, the value of \cs{@currname} used. This command
+% may be used within a package to set options before or after using
+% \cs{ProcessKeyOptions}/\cs{ProcessKeyPackageOptions}.
+% \end{function}
+%
+% \StopEventually{}
+%
+% \subsection{Implementation of \pkg{ltkeys}}
+%
+% \begin{macrocode}
+%<@@=keys>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*2ekernel>
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ExplSyntaxOn
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_generate_variant:Nn \clist_put_right:Nn { Nv }
+% \end{macrocode}
+%
+% \begin{macro}{\l_@@_options_clist}
+% A single list is used for all options, into which they are collected
+% before processing.
+% \begin{macrocode}
+\clist_new:N \l_@@_options_clist
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_options_loading_bool}
+% Used to indicate we are in the loading phase: controls the outcome
+% of warnings.
+% \begin{macrocode}
+\bool_new:N \l_@@_options_loading_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_options:Nn, \@@_options:NV}
+% \begin{macro}{\@@_options_end:}
+% The main function calls functions to collect up the global and local
+% options into \cs{l_@@_options_clist} before calling the
+% underlying functions to actually do the processing. So that a suitable
+% message is produced if the option is unknown, the special
+% \texttt{unknown} key is set if it does not already exist for the
+% current family, and is cleaned up afterwards if required. To allow
+% the \LaTeXe{} layer to know this mechanism is active, and to deal
+% with the key family not matching the file name, we store the family
+% in all cases.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options:Nn #1#2
+ {
+ \cs_gset_nopar:cpn { opt at fam@\@currname.\@currext } {#2}
+ \cs_set_protected:Npn \@@_option_end: { }
+ \clist_clear:N \l_@@_options_clist
+ \@@_options_global:Nn #1 {#2}
+ \@@_options_local:
+ \keys_if_exist:nnF {#2} { unknown }
+ {
+ \keys_define:nn {#2}
+ {
+ unknown .code:n =
+ {
+ \msg_error:nnxx { keys } { option-unknown }
+ { \l_keys_key_str } { \@currname }
+ }
+ }
+ \cs_set_protected:Npn \@@_option_end:
+ { \keys_define:nn {#2} { unknown .undefine: } }
+ }
+ \bool_set_true:N \l_@@_options_loading_bool
+ \keys_set:nV {#2} \l_@@_options_clist
+ \bool_set_false:N \l_@@_options_loading_bool
+ \cs_set_eq:NN \@unprocessedoptions \scan_stop:
+ \@@_option_end:
+ \@@_options_loaded:n {#2}
+ }
+\cs_generate_variant:Nn \@@_options:Nn { NV }
+\msg_new:nnnn { keys } { option-unknown }
+ { Unknown~option~'#1'~for~package~#2. }
+ {
+ LaTeX~has~been~asked~to~set~an~option~called~'#1'~
+ but~the~#2~package~has~not~created~an~option~with~this~name.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_options_global:Nn}
+% Global (class) options are handled differently for \LaTeXe{} packages
+% and classes. Hence this function is essentially a check on the current
+% file type. The initial test is needed as \LaTeXe{} allows variables to
+% be equal to \cs{scan_stop:}, which is usually forbidden in \pkg{expl3}
+% code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options_global:Nn #1#2
+ {
+ \cs_if_eq:NNF \@classoptionslist \scan_stop:
+ {
+ \cs_if_eq:NNTF \@currext \@clsextension
+ { \@@_options_class:n {#2} }
+ {
+ \bool_if:NT #1
+ { \@@_options_package:n {#2} }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_options_class:n}
+% For classes, each option (stripped of any content after |=|)
+% is checked for existence as a key. If found, the option is added to
+% the combined list for processing. On the other hand, unused options
+% are stored up in \cs{@unusedoptionlist}. Before any of that, though,
+% there is a simple check to see if there is an |unknown| key. If there
+% is, then \emph{everything} will match and the mapping can be skipped.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options_class:n #1
+ {
+ \cs_if_free:cF { opt@ \@currname . \@currext }
+ {
+ \keys_if_exist:nnTF {#1} { unknown }
+ {
+ \clist_put_right:Nv \l_@@_options_clist
+ { opt@ \@currname . \@currext }
+ }
+ {
+ \clist_map_inline:cn { opt@ \@currname . \@currext }
+ {
+ \keys_if_exist:neTF
+ {#1} { \@@_remove_equals:n {##1} }
+ { \clist_put_right:Nn \l_@@_options_clist {##1} }
+ { \clist_put_right:Nn \@unusedoptionlist {##1} }
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_options_package:n}
+% For global options when processing a package, the tasks are slightly
+% different from those for a class. The check is the same, but here
+% there is nothing to do if the option is not applicable. Each valid
+% option also needs to be removed from \cs{@unusedoptionlist}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options_package:n #1
+ {
+ \clist_map_inline:Nn \@classoptionslist
+ {
+ \keys_if_exist:neT {#1} { \@@_remove_equals:n {##1} }
+ {
+ \clist_put_right:Nn \l_@@_options_clist {##1}
+ \clist_remove_all:Nn \@unusedoptionlist {##1}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_options_local:}
+% If local options are found, the are added to the processing list.
+% \LaTeXe{} stores options for each file in a macro which may or may not
+% exist, hence the need to use \cs{cs_if_exist:c}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options_local:
+ {
+ \cs_if_eq:NNF \@currext \@clsextension
+ {
+ \cs_if_exist:cT { opt@ \@currname . \@currext }
+ {
+ \clist_put_right:Nv \l_@@_options_clist
+ { opt@ \@currname . \@currext }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_remove_equals:n}
+% \begin{macro}[EXP]{\@@_remove_equals:w}
+% As the name suggests, this is a simple function to remove an equals
+% sign from the input. This is all wrapped up in an \texttt{n} function
+% so that there will always be a sign available.
+% \begin{macrocode}
+\cs_new:Npn \@@_remove_equals:n #1
+ { \@@_remove_equals:w #1 = \s_@@_stop }
+\cs_new:Npn \@@_remove_equals:w #1 = #2 \s_@@_stop { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{The document interfaces}
+%
+% \begin{macro}{\DeclareKeys}
+% Defining key options is quite straight-forward: we have an intermediate
+% function to allow for potential set-up steps.
+% \begin{macrocode}
+\NewDocumentCommand \DeclareKeys { o +m }
+ {
+ \IfNoValueTF {#1}
+ { \exp_args:NV \keys_define:nn \@currname }
+ { \keys_define:nn {#1} }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ProcessKeyOptions, \ProcessKeyPackageOptions}
+% We need to deal with the older interface from \pkg{l3keys2e} here: it had
+% a mandatory argument. We can mop that up using a look-ahead, and then
+% exploit that information to determine whether the package option handling
+% is set up for the new approach for clash handling.
+% \begin{macrocode}
+\NewDocumentCommand \ProcessKeyOptions { o }
+ {
+ \IfNoValueTF {#1}
+ { \@@_options:NV \c_true_bool \@currname }
+ { \@@_options:Nn \c_true_bool {#1} }
+ }
+\NewDocumentCommand \ProcessKeyPackageOptions { o }
+ {
+ \IfNoValueTF {#1}
+ { \@@_options:NV \c_false_bool \@currname }
+ { \@@_options:Nn \c_false_bool {#1} }
+ }
+\@onlypreamble \ProcessKeyOptions
+\@onlypreamble \ProcessKeyPackageOptions
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Option usage scope}
+%
+% \begin{macro}{\@@_options_loaded:n}
+% \begin{macro}{\@@_options_loaded:nn}
+% Indicates that the load-time options for a package have been processed:
+% once this has happened, make them unavailable either with a warning or
+% an error.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options_loaded:n #1
+ {
+ \prop_get:NnNT \l_keys_usage_load_prop {#1} \l_@@_tmpa_tl
+ {
+ \clist_map_inline:Nn \l_@@_tmpa_tl
+ {
+ \keys_define:nn {#1}
+ {
+ ##1 .code:n =
+ \@@_options_loaded:nn {#1} {##1}
+ }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_options_loaded:nn #1#2
+ {
+ \bool_if:NTF \l_@@_options_loading_bool
+ {
+ \msg_warning:nnxx { keys } { load-option-ignored }
+ { \use:c { opt at fam@\@currname.\@currext } } {#2}
+ }
+ { \msg_error:nnnn { keys } { load-only } {#1} {#2} }
+ }
+\msg_new:nnn { keys } { load-option-ignored }
+ { Package~"#1"~has~already~been~loaded:~ignoring~load-time~option~"#2". }
+\msg_new:nnnn { keys } { load-only }
+ { Key~"#2"~may~only~be~used~in~the~during~loading~of~package~"#1". }
+ {
+ LaTeX~was~asked~to~set~a~key~called~"#2",~but~this~is~only~allowed~
+ in~the~optional~argument~when~loading~package~"#1".
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% Disable all preamble options in one shot.
+% \begin{macrocode}
+\tl_gput_left:Nn \@kernel at after@begindocument
+ {
+ \prop_map_inline:Nn \l_keys_usage_preamble_prop
+ {
+ \clist_map_inline:nn {#2}
+ {
+ \keys_define:nn {#1}
+ {
+ ##1 .code:n =
+ \msg_error:nnn { keys } { preamble-only } {##1}
+ }
+ }
+ }
+ }
+\msg_new:nnnn { keys } { preamble-only }
+ { Key~"#1"~may~only~be~used~in~the~preamble. }
+ {
+ LaTeX~was~asked~to~set~a~key~called~"#1",~but~this~is~only~allowed~
+ before~\begin{document}.~You~will~need~to~set~the~key~earlier.
+ }
+% \end{macrocode}
+%
+% \subsection{General key setting}
+%
+% \begin{macro}{\SetKeys}
+% A simple wrapper.
+% \begin{macrocode}
+\NewDocumentCommand \SetKeys { o +m }
+ {
+ \IfNoValueTF {#1}
+ { \keys_set:Vn \@currname }
+ { \keys_set:nn {#1} }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\ExplSyntaxOff
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</2ekernel>
+% \end{macrocode}
\ No newline at end of file
diff --git a/base/testfiles-lthooks/ltcmdhooks-001.tlg b/base/testfiles-lthooks/ltcmdhooks-001.tlg
index ae69a703..e8396389 100644
--- a/base/testfiles-lthooks/ltcmdhooks-001.tlg
+++ b/base/testfiles-lthooks/ltcmdhooks-001.tlg
@@ -58,7 +58,7 @@ l. ...\ShowHook{cmd/foo/after}
#1#2->FOO #1 #2.
l. ...\show\foo
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ...\show\@kernel at after@begindocument
Update code for hook 'para/before' on input line ...:
Update code for hook 'para/after' on input line ...:
diff --git a/base/testfiles/github-0710.lvt b/base/testfiles/github-0710.lvt
new file mode 100644
index 00000000..0a2c8e17
--- /dev/null
+++ b/base/testfiles/github-0710.lvt
@@ -0,0 +1,50 @@
+\documentclass{article}
+\begin{filecontents}[overwrite]{mypkg.sty}
+\DeclareKeys{
+ load-option-A .store = \my at A ,
+ load-option-A .usage = load ,
+ load-option-B .store = \my at B ,
+ load-option-B .usage = load ,
+ general-option-C .store = \my at C ,
+ preamble-option-D .store = \my at D ,
+ preamble-option-D .usage = preamble
+}
+\ProcessKeyOptions
+\newcommand\mypkgtest{%
+ \begingroup
+ \edef\x{\my at A:\my at B:\my at C:\my at D}%
+ \show\x
+ \endgroup
+}
+\end{filecontents}
+\input{test2e}
+
+\START
+\AUTHOR{Joseph Wright}
+
+\usepackage[load-option-A = 111]{mypkg} % 1
+\mypkgtest
+\usepackage[load-option-A = 222]{mypkg} % 2
+\mypkgtest
+\usepackage[load-option-A = 333, general-option-C = 333]{mypkg} % 3
+\mypkgtest
+\usepackage{mypkg} % 4
+\mypkgtest
+\usepackage[load-option-B = 555]{mypkg} % 5
+\mypkgtest
+\SetKeys[mypkg]{load-option-A = 666} % 6
+\mypkgtest
+\usepackage[preamble-option-D = 777]{mypkg} % 7
+\mypkgtest
+\SetKeys[mypkg]{preamble-option-D = 888} % 8
+\mypkgtest
+
+\OMIT
+\begin{document}
+\TIMO
+
+\SetKeys[mypkg]{general-option-C = 999,
+ preamble-option-D = 999} % 9
+\mypkgtest
+
+\END
diff --git a/base/testfiles/github-0710.tlg b/base/testfiles/github-0710.tlg
new file mode 100644
index 00000000..58c2c762
--- /dev/null
+++ b/base/testfiles/github-0710.tlg
@@ -0,0 +1,70 @@
+This is a generated file for the LaTeX2e validation system.
+Don't change this file in any respect.
+Author: Joseph Wright
+(mypkg.sty)
+> \x=macro:
+->111:::.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+LaTeX3 Warning: Package "mypkg" has already been loaded: ignoring load-time
+(LaTeX3) option "load-option-A".
+> \x=macro:
+->111:::.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+LaTeX3 Warning: Package "mypkg" has already been loaded: ignoring load-time
+(LaTeX3) option "load-option-A".
+> \x=macro:
+->111::333:.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+> \x=macro:
+->111::333:.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+LaTeX3 Warning: Package "mypkg" has already been loaded: ignoring load-time
+(LaTeX3) option "load-option-B".
+> \x=macro:
+->111::333:.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+! LaTeX3 Error: Key "load-option-A" may only be used in the during loading of
+(LaTeX3) package "mypkg".
+For immediate help type H <return>.
+ ...
+l. ...\SetKeys[mypkg]{load-option-A = 666}
+ % 6
+LaTeX was asked to set a key called "load-option-A", but this is only allowed
+in the optional argument when loading package "mypkg".
+> \x=macro:
+->111::333:.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+> \x=macro:
+->111::333:777.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+> \x=macro:
+->111::333:888.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
+! LaTeX3 Error: Key "preamble-option-D" may only be used in the preamble.
+For immediate help type H <return>.
+ ...
+l. ... preamble-option-D = 999}
+ % 9
+LaTeX was asked to set a key called "preamble-option-D", but this is only
+allowed before \begin {document}. You will need to set the key earlier.
+> \x=macro:
+->111::999:888.
+\mypkgtest ...my at A :\my at B :\my at C :\my at D }\show \x
+ \endgroup
+l. ...\mypkgtest
diff --git a/base/testfiles/tlb-rollback-005.luatex.tlg b/base/testfiles/tlb-rollback-005.luatex.tlg
index a15b1e85..b8fe3e6c 100644
--- a/base/testfiles/tlb-rollback-005.luatex.tlg
+++ b/base/testfiles/tlb-rollback-005.luatex.tlg
@@ -1,7 +1,7 @@
This is a generated file for the l3build validation system.
Don't change this file in any respect.
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ...\makeatletter\show\@kernel at after@begindocument
\makeatother
(latexrelease.sty
@@ -709,7 +709,7 @@ Applying: [....-..-..] UTF-8 default on input line ....
Already applied: [....-..-..] UTF-8 default on input line ....
)
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ...\makeatletter\show\@kernel at after@begindocument
\makeatother
(minimal.cls
diff --git a/base/testfiles/tlb-rollback-005.tlg b/base/testfiles/tlb-rollback-005.tlg
index 7e870ab6..62104dca 100644
--- a/base/testfiles/tlb-rollback-005.tlg
+++ b/base/testfiles/tlb-rollback-005.tlg
@@ -1,7 +1,7 @@
This is a generated file for the l3build validation system.
Don't change this file in any respect.
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ...\makeatletter\show\@kernel at after@begindocument
\makeatother
(latexrelease.sty
@@ -1132,7 +1132,7 @@ Now handling font encoding U ...
Already applied: [....-..-..] UTF-8 default on input line ....
)
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ......eatletter\show\@kernel at after@begindocument
\makeatother
(minimal.cls
diff --git a/base/testfiles/tlb-rollback-005.xetex.tlg b/base/testfiles/tlb-rollback-005.xetex.tlg
index 84352cad..3247a60a 100644
--- a/base/testfiles/tlb-rollback-005.xetex.tlg
+++ b/base/testfiles/tlb-rollback-005.xetex.tlg
@@ -1,7 +1,7 @@
This is a generated file for the l3build validation system.
Don't change this file in any respect.
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ...\makeatletter\show\@kernel at after@begindocument
\makeatother
(latexrelease.sty
@@ -706,7 +706,7 @@ Applying: [....-..-..] UTF-8 default on input line ....
Already applied: [....-..-..] UTF-8 default on input line ....
)
> \@kernel at after@begindocument=macro:
-->\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
+->\prop_map_inline:Nn \l_keys_usage_preamble_prop {\clist_map_inline:nn {##2}{\keys_define:nn {##1}{####1.code:n=\msg_error:nnn {keys}{preamble-only}{####1}}}}\__hook_cmd_begindocument_code: \bool_gset_true:N \g__pdf_init_bool .
l. ......eatletter\show\@kernel at after@begindocument
\makeatother
(minimal.cls
More information about the latex3-commits
mailing list.