[latex3-commits] [git/LaTeX3-latex3-latex2e] options: Add l3keys2e code as ltkeys (6553bc1d)
Joseph Wright
joseph.wright at morningstar2.co.uk
Fri Nov 26 13:10:03 CET 2021
Repository : https://github.com/latex3/latex2e
On branch : options
Link : https://github.com/latex3/latex2e/commit/6553bc1df261060425fd737d611fff78fb105b45
>---------------------------------------------------------------
commit 6553bc1df261060425fd737d611fff78fb105b45
Author: Joseph Wright <joseph.wright at morningstar2.co.uk>
Date: Fri Nov 26 11:57:08 2021 +0000
Add l3keys2e code as ltkeys
There are a few internal changes, but the main obvious
one is that the family name is made optional:
this will likely help 'detect' that code has been updated.
>---------------------------------------------------------------
6553bc1df261060425fd737d611fff78fb105b45
base/doc/ltnews35.tex | 24 ++++-
base/doc/source2e.tex | 2 +
base/format.ins | 1 +
base/ltkeys.dtx | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 312 insertions(+), 2 deletions(-)
diff --git a/base/doc/ltnews35.tex b/base/doc/ltnews35.tex
index 6b5d4816..7b535667 100644
--- a/base/doc/ltnews35.tex
+++ b/base/doc/ltnews35.tex
@@ -139,6 +139,28 @@
\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 \pkg{expl3} 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}
+\ProcessKeysOptions
+\end{verbatim}
+will now automatically pick up the package name as the key \emph{family}, which
+can otherwise be given as an optional argument
+\begin{verbatim}
+\ProcessKeysOptions[family]
+\end{verbatim}
+
\subsection{???}
%
\githubissue{???}
@@ -228,5 +250,3 @@ interfaces for user defined \texttt{list} callbacks.
\end{document}
-
-
diff --git a/base/doc/source2e.tex b/base/doc/source2e.tex
index abe8c1aa..969b6ddc 100644
--- a/base/doc/source2e.tex
+++ b/base/doc/source2e.tex
@@ -326,6 +326,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 eece2f57..ae601a85 100644
--- a/base/format.ins
+++ b/base/format.ins
@@ -203,6 +203,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/ltkeys.dtx b/base/ltkeys.dtx
new file mode 100644
index 00000000..4cea46da
--- /dev/null
+++ b/base/ltkeys.dtx
@@ -0,0 +1,287 @@
+% \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/04/20 v1.3c 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.
+%
+% \begin{function}{\ProcessKeysOptions}
+% \begin{syntax}
+% \cs{ProcessKeysOptions} \oarg{family}
+% \end{syntax}
+% The \cs{ProcessKeysOptions} function is used to check the current
+% option list against the keys defined for \Arg{module}. Global (class)
+% options and local (package) options are checked when this function
+% is called in a package.
+% \end{function}
+%
+% \begin{function}{\ProcessKeysPackageOptions}
+% \begin{syntax}
+% \cs{ProcessKeysPackageOptions} \oarg{family}
+% \end{syntax}
+% This function works in a similar manner to \cs{ProcessKeysOptions}.
+% When used in a package, \cs{ProcessKeysPackageOptions}
+% will not examine any global (class) class options available. In contrast,
+% \cs{ProcessKeysOptions} does parse class options (in common with the
+% classical \cs{ProcessOptions} command).
+% \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{macro}{\@@_options:Nn}
+% \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.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_options:Nn #1#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 { keyvalue } { option-unknown }
+ { \l_keys_key_str } { \@currname }
+ }
+ }
+ \cs_set_protected:Npn \@@_option_end:
+ { \keys_define:nn {#2} { unknown .undefine: } }
+ }
+ \keys_set:nn {#2} \l_@@_options_clist
+ \cs_set_eq:NN \@unprocessedoptions \scan_stop:
+ \@@_option_end:
+ }
+% \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:nxTF
+ {#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:nxT {#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}{\ProcessKeysOptions}
+% \begin{macro}{\ProcessKeysPackageOptions}
+% 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 \ProcessKeysOptions { o }
+ {
+ \IfNoValueTF {#1}
+ { \@@_options_group_check:N \c_true_bool }
+ { \@@_options:Nn \c_true_bool {#1} }
+ }
+\NewDocumentCommand \ProcessKeysPackageOptions { o }
+ {
+ \IfNoValueTF {#1}
+ { \@@_options_group_check:N \c_false_bool }
+ { \@@_options:Nn \c_false_bool {#1} }
+ }
+\@onlypreamble \ProcessKeysOptions
+\@onlypreamble \ProcessKeysPackageOptions
+\cs_new_protected:Npn \@@_options_group_check:N #1
+ {
+ \peek_meaning:NTF \c_group_begin_token
+ { \@@_options:Nn #1 }
+ { \exp_args:NV \@@_options:Nn #1 \@currname }
+ }
+% \end{macrocode}
+%\end{macro}
+%\end{macro}
+%
+% \begin{macrocode}
+\ExplSyntaxOff
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</2ekernel>
+% \end{macrocode}
\ No newline at end of file
More information about the latex3-commits
mailing list.