[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.