[latex3-commits] [latex3/latex2e] UF-latexlab-toc, defaultT1, develop, gh-1265, gh1183, main, tlc3-errata, update-UseTaggingSocket: Add mech to exclude class options from package processing (5dd025c1f)
github at latex-project.org
github at latex-project.org
Sat Jul 13 18:02:17 CEST 2024
- Previous message (by thread): [latex3-commits] [latex3/latex2e] UF-latexlab-toc, defaultT1, develop, gh-1265, gh1183, main, tlc3-errata, update-UseTaggingSocket: Update l3kernel to 2024-06-19 (d8793152c)
- Next message (by thread): [latex3-commits] [latex3/latex2e] UF-latexlab-toc, defaultT1, develop, gh-1265, gh1183, main, tlc3-errata, update-UseTaggingSocket: errata TLC3 (e2a0854a6)
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Repository : https://github.com/latex3/latex2e
On branches: UF-latexlab-toc,defaultT1,develop,gh-1265,gh1183,main,tlc3-errata,update-UseTaggingSocket
Link : https://github.com/latex3/latex2e/commit/5dd025c1fc8898cdc0558cbc2b0f1bde9fcc8730
>---------------------------------------------------------------
commit 5dd025c1fc8898cdc0558cbc2b0f1bde9fcc8730
Author: Joseph Wright <joseph at texdev.net>
Date: Wed Jun 19 08:27:42 2024 +0100
Add mech to exclude class options from package processing
>---------------------------------------------------------------
5dd025c1fc8898cdc0558cbc2b0f1bde9fcc8730
base/changes.txt | 5 ++
base/doc/clsguide.tex | 5 +-
base/doc/ltnews40.tex | 29 +++++++++
base/ltkeys.dtx | 136 ++++++++++++++++++++++++++++++++++-------
base/testfiles/github-0892.lvt | 3 +-
base/testfiles/github-1279.lvt | 43 +++++++++++++
base/testfiles/github-1279.tlg | 29 +++++++++
7 files changed, 227 insertions(+), 23 deletions(-)
diff --git a/base/changes.txt b/base/changes.txt
index 0fa283c39..4fd4b7020 100644
--- a/base/changes.txt
+++ b/base/changes.txt
@@ -6,6 +6,11 @@ to completeness or accuracy and it contains some references to files that are
not part of the distribution.
================================================================================
+2024-06-19 Joseph Wright <Joseph.Wright at latex-project.org>
+ * ltkeys.dtx, clsguide.tex
+ Refactor class option code
+ Add mechanism to exclude class options from package processing
+
2024-06-17 David Carlisle <David.Carlisle at latex-project.org>
* ltfssbas.dtx: set \tracinglostchars in \showhyphens. gh/1380
diff --git a/base/doc/clsguide.tex b/base/doc/clsguide.tex
index 6b9e49c4e..c7c2ac5e2 100644
--- a/base/doc/clsguide.tex
+++ b/base/doc/clsguide.tex
@@ -42,7 +42,7 @@
\texttt{clsguide.tex} for full details.}%
}
-\date{2024-05-24}
+\date{2024-06-19}
\NewDocumentCommand\cs{m}{\texttt{\textbackslash\detokenize{#1}}}
\NewDocumentCommand\marg{m}{\arg{#1}}
@@ -841,6 +841,9 @@ The basic properties provided here are
\item \texttt{.code} --- execute arbitrary code
\item \texttt{.if} --- sets a \TeX{} |\if...| switch
\item \texttt{.ifnot} --- sets an inverted \TeX{} |\if...| switch
+ \item \texttt{.pass-to-packages} --- for class options, this specifies
+ whether the option should be treated \enquote{global} (read by packages
+ from the global list); for package options this property has no effect
\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
diff --git a/base/doc/ltnews40.tex b/base/doc/ltnews40.tex
index f9755ed81..10384e1a8 100644
--- a/base/doc/ltnews40.tex
+++ b/base/doc/ltnews40.tex
@@ -149,6 +149,35 @@
\section{Code improvements}
+\subsection{Avoiding keyval option clashes between classes and packages}
+
+In \LaTeX{} News~35~\cite{40:ltnews35} we introduced keyval option processing
+to the kernel. Following the standard for \LaTeXe{} options, keyval options
+given to the \cs{documentclass} line were treated as global and so parsed by
+every package. However, with keyvals, the likelihood of a name clash between a
+class-specific option and one used by a package is much higher than it is with
+simple strings. We have therefore refined the mechanism in the current release.
+
+When a class uses the kernel keyval processor, any options it recognises are
+recorded and any packages using the keyval processor will then \emph{skip}
+these \enquote{global} options. To allow for the case where a class directly
+uses an option which should be global (for example \texttt{draft}), a new key
+property \texttt{.pass-to-packages} has been added. This can then be set to
+indicate that this key is not to be skipped. For example
+\begin{verbatim}
+\DeclareKeys{
+ draft .if = {ifl at cls@draft},
+ draft .pass-to-packages = true,
+ mode .store = \cls at mode
+}
+\end{verbatim}
+in a class would create two options, \texttt{draft} and \texttt{mode}. The
+\texttt{draft} option will be treated in the normal way by packages using
+keyvals, but they will ignore the \texttt{mode} option: it is effectively
+marked as \enquote{private} to the class.
+
+\githubissue{1279}
+
\section{Bug fixes}
\subsection{Fix wrong file type in a rollback warning}
diff --git a/base/ltkeys.dtx b/base/ltkeys.dtx
index 34a0ab4b4..657248c01 100644
--- a/base/ltkeys.dtx
+++ b/base/ltkeys.dtx
@@ -33,7 +33,7 @@
%<*driver>
% \fi
\ProvidesFile{ltkeys.dtx}
- [2024/01/13 v1.0m LaTeX Kernel (Keyval options)]
+ [2024/06/19 v1.0n LaTeX Kernel (Keyval options)]
% \iffalse
\documentclass{l3doc}
\GetFileInfo{ltkeys.dtx}
@@ -73,6 +73,9 @@
% \item \texttt{.code} --- execute arbitrary code
% \item \texttt{.if} --- sets a \TeX{} \cs{if...} switch
% \item \texttt{.ifnot} --- sets an inverted \TeX{} \cs{if...} switch
+% \item \texttt{.pass-to-packages} --- for class options, this specifies
+% whether the option should be treated \enquote{global} (read by packages
+% from the global list); for package options this property has no effect
% \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
@@ -182,12 +185,75 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}{.pass-to-packages}
+% \changes{v1.0n}{2024/06/19}{New key property}
+% \begin{macro}{\@@_scope:n}
+% \changes{v1.0n}{2024/06/19}{New function}
+% \begin{macro}{\@@_scope:N}
+% \changes{v1.0n}{2024/06/19}{New function}
+% \begin{macro}{\@@_scope:w}
+% \changes{v1.0n}{2024/06/19}{New function}
+% Used to force options to be global: as this property (uniquely) has
+% an \emph{optional} value, there is a bit of work to do.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_str .pass-to-packages }
+ {
+ \bool_if:NTF \l_@@_no_value_bool
+ { \@@_scope:n { true } }
+ { \@@_scope:n }
+ }
+\cs_new_protected:Npn \@@_scope:n #1
+ {
+ \str_case:nnF {#1}
+ {
+ { true }
+ { \@@_scope:N \clist_put_right:Ne }
+ { false }
+ { \@@_scope:N \clist_remove_all:Ne }
+ }
+ {
+ \msg_error:nnnn { keys }
+ { choice-unknown }
+ { .pass-to-packages }
+ {#1}
+ }
+ }
+\cs_new_protected:Npn \@@_scope:N #1
+ {
+ #1 \l_@@_forced_global_clist
+ { \exp_after:wN \@@_scope:w \l_keys_path_str \s_@@_stop }
+ }
+\cs_new:Npn \@@_scope:w #1 / #2 \s_@@_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Main mechanism}
%
% \begin{macrocode}
+\cs_generate_variant:Nn \clist_if_in:NnT { Ne }
+\cs_generate_variant:Nn \clist_if_in:NnTF { Ne }
\cs_generate_variant:Nn \clist_put_right:Nn { Nv }
% \end{macrocode}
%
+% \begin{macro}{\l_@@_class_only_clist}
+% \changes{v1.0n}{2024/06/19}{New variable}
+% Used to track class-only options.
+% \begin{macrocode}
+\clist_new:N \l_@@_class_only_clist
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\l_@@_forced_global_clist}
+% \changes{v1.0n}{2024/06/19}{New variable}
+% Used to force options to be global.
+% \begin{macrocode}
+\clist_new:N \l_@@_forced_global_clist
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}{\l_@@_options_clist}
% A single list is used for all options, into which they are collected
% before processing.
@@ -295,33 +361,34 @@
% \changes{v1.0h}{2022/06/19}{Further work on handling of option removal}
% \changes{v1.0h}{2022/06/20}{Use raw options data}
% \changes{v1.0m}{2024/01/13}{Trim spaces off key names}
+% \changes{v1.0n}{2024/06/19}{Refactor function}
% \begin{macro}{\@@_options_class:nnn}
% \changes{v1.0h}{2022/06/20}{New function}
% \changes{v1.0i}{2022/07/05}{Correct naming of raw class options storage}
% \changes{v1.0l}{2022/10/22}{Correct handling of unused option list}
+% \changes{v1.0n}{2024/06/19}{Refactor function}
+% \changes{v1.0n}{2024/06/19}{Track options used by classes}
+% \begin{macro}{\@@_options_class:nn}
+% \changes{v1.0n}{2024/06/19}{New function}
% 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.
+% are stored up in \cs{@unusedoptionlist}. An earlier version of
+% this code checked for the \texttt{unknown} key just once and
+% if found short-cutted the loop: that though makes handling more
+% complex situations harder, so we take the performance hit instead.
+% Options used by classes are tracked but the catch-all \texttt{unknown}
+% is excluded (hence not using a lazy evaluation for the key testing).
% \begin{macrocode}
\cs_new_protected:Npn \@@_options_class:n #1
{
\cs_if_free:cF { @raw at opt@ \@currname . \@currext }
{
- \keys_if_exist:nnTF {#1} { unknown }
+ \clist_map_inline:cn { @raw at opt@ \@currname . \@currext }
{
- \clist_put_right:Nv \l_@@_options_clist
- { @raw at opt@ \@currname . \@currext }
- }
- {
- \clist_map_inline:cn { @raw at opt@ \@currname . \@currext }
- {
- \exp_args:Ne \@@_options_class:nnn
- { \tl_trim_spaces:e { \@@_remove_equals:n {##1} } }
- {##1} {#1}
- }
+ \exp_args:Ne \@@_options_class:nnn
+ { \tl_trim_spaces:e { \@@_remove_equals:n {##1} } }
+ {##1} {#1}
}
}
}
@@ -329,17 +396,27 @@
{
\keys_if_exist:nnTF {#3} {#1}
{
- \clist_put_right:Nn \l_@@_options_clist {#2}
- \clist_remove_all:Nn \@unusedoptionlist {#1}
+ \@@_options_class:nn {#1} {#2}
+ \clist_put_right:Ne \l_@@_class_only_clist { \tl_to_str:n {#1} }
}
{
- \clist_if_in:NnF \@unusedoptionlist {#1}
- { \clist_put_right:Nn \@unusedoptionlist {#1} }
+ \keys_if_exist:nnTF {#3} { unknown }
+ { \@@_options_class:nn {#1} {#2} }
+ {
+ \clist_if_in:NnF \@unusedoptionlist {#1}
+ { \clist_put_right:Nn \@unusedoptionlist {#1} }
+ }
}
}
+\cs_new_protected:Npn \@@_options_class:nn #1#2
+ {
+ \clist_remove_all:Nn \@unusedoptionlist {#1}
+ \clist_put_right:Nn \l_@@_options_clist {#2}
+ }
% \end{macrocode}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% \begin{macro}{\@@_options_package:n}
% \changes{v1.0g}{2022/06/16}{Better handling of option removal}
@@ -348,6 +425,9 @@
% \changes{v1.0m}{2024/01/13}{Trim spaces off key names}
% \begin{macro}{\@@_options_package:nnn}
% \changes{v1.0h}{2022/06/19}{New function}
+% \changes{v1.0n}{2024/06/19}{Skip options given to packages}
+% \begin{macro}{\@@_options_package:nn}
+% \changes{v1.0n}{2024/06/19}{New functions}
% 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
@@ -362,17 +442,31 @@
{##1} {#1}
}
}
+% \end{macrocode}
+% The forced-global test here needs to use \cs{tl_to_str:n} as the data come
+% from a key name, which is always a string.
+% \begin{macrocode}
\cs_new_protected:Npn \@@_options_package:nnn #1#2#3
{
\keys_if_exist:nnT {#3} {#1}
{
- \clist_put_right:Nn \l_@@_options_clist {#2}
- \clist_remove_all:Nn \@unusedoptionlist {#1}
+ \clist_if_in:NeTF \l_@@_class_only_clist { \tl_to_str:n {#1} }
+ {
+ \clist_if_in:NeT \l_@@_forced_global_clist { \tl_to_str:n {#1} }
+ { \@@_options_package:nn {#1} {#2} }
+ }
+ { \@@_options_package:nn {#1} {#2} }
}
}
+\cs_new_protected:Npn \@@_options_package:nn #1#2
+ {
+ \clist_put_right:Nn \l_@@_options_clist {#2}
+ \clist_remove_all:Nn \@unusedoptionlist {#1}
+ }
% \end{macrocode}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% \begin{macro}{\@@_options_local:}
% \changes{v1.0h}{2022/06/20}{Use raw options data}
diff --git a/base/testfiles/github-0892.lvt b/base/testfiles/github-0892.lvt
index 3f3922034..3fa5344f0 100644
--- a/base/testfiles/github-0892.lvt
+++ b/base/testfiles/github-0892.lvt
@@ -3,7 +3,8 @@
%% \RequirePackage{l3keys2e}
\ProvidesExplClass{l3keys2e-class}{0000/00/00}{0.0}{test}
\DeclareKeys {
- option1 .code:n = {\wlog{You~gave~`#1'~for~option1}}
+ option1 .code:n = {\wlog{You~gave~`#1'~for~option1}},
+ option1 .pass-to-packages
}
\ProcessKeyOptions
\end{filecontents}
diff --git a/base/testfiles/github-1279.lvt b/base/testfiles/github-1279.lvt
new file mode 100644
index 000000000..8bc150d97
--- /dev/null
+++ b/base/testfiles/github-1279.lvt
@@ -0,0 +1,43 @@
+\begin{filecontents}[force]{\jobname.cls}
+\newcommand*\l at cls@a at tl{}
+\newcommand*\l at cls@b at tl{}
+\DeclareKeys{
+ option-a .store = \l at cls@a at tl,
+ option-a .pass-to-packages,
+ option-b .store = \l at cls@b at tl,
+ option-b .pass-to-packages = true,
+ option-c .store = \l at cls@c at tl,
+ option-c .pass-to-packages = false,
+ option-d .store = \l at cls@d at tl
+ }
+\ProcessKeyOptions
+\show\l at cls@a at tl
+\show\l at cls@b at tl
+\show\l at cls@c at tl
+\show\l at cls@d at tl
+\end{filecontents}
+\begin{filecontents}[force]{\jobname.sty}
+\newcommand*\l at pkg@a at tl{}
+\newcommand*\l at pkg@b at tl{}
+\newcommand*\l at pkg@c at tl{}
+\newcommand*\l at pkg@d at tl{}
+\DeclareKeys{
+ option-a .store = \l at pkg@a at tl,
+ option-b .store = \l at pkg@b at tl,
+ option-c .store = \l at pkg@c at tl,
+ option-d .store = \l at pkg@d at tl
+ }
+\ProcessKeyOptions
+\show\l at pkg@a at tl
+\show\l at pkg@b at tl
+\show\l at pkg@c at tl
+\show\l at pkg@d at tl
+\end{filecontents}
+
+\input{test2e}
+
+\START
+\documentclass[option-a = AAA, option-b = BBB, option-c = CCC, option-d = DDD]{\jobname}
+\usepackage{\jobname}
+
+\END
diff --git a/base/testfiles/github-1279.tlg b/base/testfiles/github-1279.tlg
new file mode 100644
index 000000000..907a940f5
--- /dev/null
+++ b/base/testfiles/github-1279.tlg
@@ -0,0 +1,29 @@
+This is a generated file for the LaTeX2e validation system.
+Don't change this file in any respect.
+(github-1279.cls
+> \l at cls@a at tl=macro:
+->AAA.
+l. ...\show\l at cls@a at tl
+> \l at cls@b at tl=macro:
+->BBB.
+l. ...\show\l at cls@b at tl
+> \l at cls@c at tl=macro:
+->CCC.
+l. ...\show\l at cls@c at tl
+> \l at cls@d at tl=macro:
+->DDD.
+l. ...\show\l at cls@d at tl
+) (github-1279.sty
+> \l at pkg@a at tl=macro:
+->AAA.
+l. ...\show\l at pkg@a at tl
+> \l at pkg@b at tl=macro:
+->BBB.
+l. ...\show\l at pkg@b at tl
+> \l at pkg@c at tl=macro:
+->.
+l. ...\show\l at pkg@c at tl
+> \l at pkg@d at tl=macro:
+->.
+l. ...\show\l at pkg@d at tl
+)
- Previous message (by thread): [latex3-commits] [latex3/latex2e] UF-latexlab-toc, defaultT1, develop, gh-1265, gh1183, main, tlc3-errata, update-UseTaggingSocket: Update l3kernel to 2024-06-19 (d8793152c)
- Next message (by thread): [latex3-commits] [latex3/latex2e] UF-latexlab-toc, defaultT1, develop, gh-1265, gh1183, main, tlc3-errata, update-UseTaggingSocket: errata TLC3 (e2a0854a6)
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the latex3-commits
mailing list.