我已被告知这个问题从 移动pgfopts
到latex3
处理包选项。我想这样做,但我必须承认我有点迷茫。我最常用的模式示例将对我有很大帮助。
问题:如何用键值系统替换下面pgfopts
的包选项管理系统?如果您发现此包中可以改进的地方,那么分两步回答会很好:mypackage
latex3
- 最直译的翻译可能
pgfopts
是latex3
- 更新后的软件包可能带来的改进
我的包.sty
% preamble
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{mypackage}[2024/01/01 MyPackage]
\RequirePackage{xparse}
\RequirePackage{pgfopts}
\makeatletter
% package options
\pgfkeys{
/mypackage/.is family,
/mypackage/.cd,
% options
option/.store in = \mypackage@option,
option = a,
eoption/.estore in = \mypackage@eoption,
eoption = b,
% color
color/.estore in = \mypackage@color,
color = ,
blue/.code = \pgfkeys{/mypackage/color = blue},
blue/.value forbidden,
red/.code = \pgfkeys{/mypackage/color = red},
red/.value forbidden,
green/.code = \pgfkeys{/mypackage/color = green},
green/.value forbidden,
% choice
choice/.is choice,
choice/.estore in = \mypackage@choice,
choice/true/.code = \pgfkeys{/mypackage/choice = true},
choice/false/.code = \pgfkeys{/mypackage/choice = false},
choice/.default = true,
choice = false,
}
\ProcessPgfPackageOptions{/mypackage}
% \print{identifier}: print current mypackage options
\ExplSyntaxOn
\NewDocumentCommand{\print}{m}{
\begin{tabular}{|l|l|}
\hline
\multicolumn{2}{|c|}{\texttt{mypackage}\ options:\ \texttt{#1}}\\
\hline
option & \mypackage@option \\
eoption & \mypackage@eoption \\
color & \mypackage@color \\
choice & \mypackage@choice \\
\hline
\end{tabular}\par\bigskip
}
\ExplSyntaxOff
% \setup{package}{option = xxxx}: set options for the specified package
\ExplSyntaxOn
\NewDocumentCommand{\setup}{mm}{\mypackage_setup:en{#1}{#2}}
\cs_new:Nn \mypackage_setup:nn{\pgfkeys{/#1,#2}}
\cs_generate_variant:Nn \mypackage_setup:nn{en}
\ExplSyntaxOff
% \reset{package}: reset options to default for the specified package
\ExplSyntaxOn
\NewDocumentCommand{\reset}{m}{\mypackage_reset:e{#1}}
\cs_new:Nn \mypackage_reset:n{\setup{#1}{#1@initial}}
\cs_generate_variant:Nn \mypackage_reset:n{e}
\ExplSyntaxOff
% \newstyle(*expanded){package}{style}{options}: create a style for the package
\ExplSyntaxOn
\NewDocumentCommand{\newstyle}{smmm}{\mypackage_newstyle:neen{#1}{#2}{#3}{#4}}
\cs_new:Nn \mypackage_newstyle:nnnn{
\IfBooleanTF{#1}{
\setup{#2}{#2@#3/.style/.expanded={#4}}
}{
\setup{#2}{#2@#3/.style={#4}}
}
}
\cs_generate_variant:Nn \mypackage_newstyle:nnnn{neen}
\ExplSyntaxOff
% \setstyle{package}{style}: set the provided style for the given package
\ExplSyntaxOn
\NewDocumentCommand{\setstyle}{mm}{\mypackage_setstyle:ee{#1}{#2}}
\cs_new:Nn \mypackage_setstyle:nn{\setup{#1}{#1@#2}}
\cs_generate_variant:Nn \mypackage_setstyle:nn{ee}
\ExplSyntaxOff
% initial style
\newstyle*{mypackage}{initial}{
option = \mypackage@option,
eoption = \mypackage@eoption,
color = \mypackage@color,
choice = \mypackage@choice,
}
% closing
\makeatother
我的文档.tex
\documentclass[varwidth = true]{standalone}
\usepackage[blue]{mypackage}
\newstyle{mypackage}{mystyle}{option = A, eoption = B, green, choice = false}
\begin{document}
\print{1}
\setup{mypackage}{red}
\print{2}
\setup{mypackage}{choice}
\print{3}
\reset{mypackage}
\print{4}
\begingroup
\setup{mypackage}{option = X, eoption = Y}
\print{5}
\endgroup
\print{6}
\setstyle{mypackage}{mystyle}
\print{7}
\end{document}
输出
答案1
好吧,在l3keys
/ltkeys
设置键和定义键是两个独立的事情,所以定义新样式的方式需要一点改变。事实上,你\setup
以一种可能用于设置任意包的键的方式定义你,这很奇怪,或者至少是不寻常的,而且没有必要,ltkeys
因为\SetKeys
已经通过其可选参数允许这样做。你的样式也是如此,你在这里允许任意注入很奇怪,在我看来,你应该将其限制在你自己包的命名空间内。
尽管如此,这或多或少只是直接复制了您的代码,只是为了使其与ltkeys
而不是一起工作pgfkeys
,没有任何进一步的改进(这些改进在本答案的后面)。请注意,并非所有您需要的处理程序都可以从层获得ltkeys
,您必须使用l3keys
它们的名称。我建议您将整个包更改为 ,而expl3
不是使用这种奇怪的 L3 和 L2e 代码混合。
\begin{filecontents}[overwrite]{\jobname.sty}
% preamble
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{\@currname}[2024/01/01 MyPackage]
\RequirePackage{xparse}
\ExplSyntaxOn
\DeclareKeys
{
% options
option .store = \mypackage@option
,option .initial:n = a
,eoption .tl_set_e:c = mypackage@eoption
,eoption .initial:n = b
% colors
,color .tl_set_e:c = mypackage@color
,blue .meta:n = { color = blue }
,blue .value_forbidden:n = true
,red .meta:n = { color = red }
,red .value_forbidden:n = true
,green .meta:n = { color = green }
,green .value_forbidden:n = true
% choice
,choice .choices:nn = { true, false }
{ \cs_set_eq:NN \mypackage@choice \l_keys_choice_tl }
,choice .default:n = true
,choice .initial:n = false
}
\ExplSyntaxOff
\ProcessKeyOptions
% \print{identifier}: print current mypackage options
\ExplSyntaxOn
\NewDocumentCommand{\print}{m}{
\begin{tabular}{|l|l|}
\hline
\multicolumn{2}{|c|}{\texttt{mypackage}\ options:\ \texttt{#1}}\\
\hline
option & \mypackage@option \\
eoption & \mypackage@eoption \\
color & \mypackage@color \\
choice & \mypackage@choice \\
\hline
\end{tabular}\par\bigskip
}
\ExplSyntaxOff
% \setup{package}{option = xxxx}: set options for the specified package
\ExplSyntaxOn
\NewDocumentCommand{\setup}{mm}{\mypackage_setup:en{#1}{#2}}
\cs_new:Nn \mypackage_setup:nn{\SetKeys[#1]{#2}}
\cs_generate_variant:Nn \mypackage_setup:nn{en}
\ExplSyntaxOff
% \reset{package}: reset options to default for the specified package
\ExplSyntaxOn
\NewDocumentCommand{\reset}{m}{\mypackage_reset:e{#1}}
\cs_new:Nn \mypackage_reset:n{\setup{#1}{#1@initial}}
\cs_generate_variant:Nn \mypackage_reset:n{e}
\ExplSyntaxOff
% \newstyle(*expanded){package}{style}{options}: create a style for the package
\ExplSyntaxOn
\NewDocumentCommand{\newstyle}{smmm}{\mypackage_newstyle:neen{#1}{#2}{#3}{#4}}
\cs_new:Nn \mypackage_newstyle:nnnn{
\IfBooleanTF{#1}{
\keys_define:ne {#2} { \exp_not:n { #2@#3 } .meta:n = {#4} }
}{
\keys_define:nn {#2} { #2@#3 .meta:n = {#4} }
}
}
\cs_generate_variant:Nn \mypackage_newstyle:nnnn{neen}
\ExplSyntaxOff
% \setstyle{package}{style}: set the provided style for the given package
\ExplSyntaxOn
\NewDocumentCommand{\setstyle}{mm}{\mypackage_setstyle:ee{#1}{#2}}
\cs_new:Nn \mypackage_setstyle:nn{\setup{#1}{#1@#2}}
\cs_generate_variant:Nn \mypackage_setstyle:nn{ee}
\ExplSyntaxOff
% initial style
\newstyle*{\@currname}{initial}{
option = \mypackage@option,
eoption = \mypackage@eoption,
color = \mypackage@color,
choice = \mypackage@choice,
}
% closing
\end{filecontents}
\documentclass[varwidth = true]{standalone}
\usepackage[blue]{\jobname}
\newstyle{\jobname}{mystyle}{option = A, eoption = B, green, choice = false}
\begin{document}
\print{1}
\setup{\jobname}{red}
\print{2}
\setup{\jobname}{choice}
\print{3}
\reset{\jobname}
\print{4}
\begingroup
\setup{\jobname}{option = X, eoption = Y}
\print{5}
\endgroup
\print{6}
\setstyle{\jobname}{mystyle}
\print{7}
\end{document}
这是您的代码版本,其中我做了一些更改(上面已经暗示过):
- 仅为您自己的包提供直接接口
- 尽可能转移到 L3 代码
- 确保
\newstyle
它是一种新风格 - 不要过度扩展 -list
reset
(使用\exp_not:V
) - 为使用的 LaTeX 内核设置更接近实际的最低日期
- 将应受保护的宏定义为受保护
\begin{filecontents}[overwrite]{\jobname.sty}
% not entirely sure this is absolutely correct, but it's a much better guess
% than 1994 (2022/06/01 it is at least due to `\keys_precompile:nnN`)
\NeedsTeXFormat{LaTeX2e}[2022/06/01]
\ProvidesExplPackage{\@currname}{2024-01-01}{0.0}{MyPackage}
\keys_define:nn { mypackage }
{
% options
option .tl_set:N = \l_mypackage_option_tl
,option .initial:n = a
,eoption .tl_set_e:N = \l_mypackage_eoption_tl
,eoption .initial:n = b
% colors
,color .tl_set_e:N = \l_mypackage_color_tl
% empty initial value is implicitly set if the tl doesn't exist yet
,blue .meta:n = { color = blue }
,blue .value_forbidden:n = true
,red .meta:n = { color = red }
,red .value_forbidden:n = true
,green .meta:n = { color = green }
,green .value_forbidden:n = true
% choice
,choice .choices:nn = { true, false }
{ \tl_set_eq:NN \l_mypackage_choice_tl \l_keys_choice_tl }
,choice .default:n = true
,choice .initial:n = false
}
\ProcessKeyOptions[mypackage]
% \print{identifier}: print current mypackage options
\NewDocumentCommand{\print}{m}{
\begin{tabular}{|l|l|}
\hline
\multicolumn{2}{|c|}{\texttt{mypackage}~ options:~ \texttt{#1}} \\
\hline
option & \l_mypackage_option_tl \\
eoption & \l_mypackage_eoption_tl \\
color & \l_mypackage_color_tl \\
choice & \l_mypackage_choice_tl \\
\hline
\end{tabular}\par\bigskip
}
% \mypackagesetup{option = xxxx}: set options for mypackage
\NewDocumentCommand \mypackagesetup { m }
{ \keys_set:nn { mypackage } {#1} }
% \newstyle(*expanded){style}{options}: create a style for the package
\NewDocumentCommand \newstyle { smm }
{
\IfBooleanTF {#1}
{ \mypackage_newstyle:ee {#2} {#3} }
{ \mypackage_newstyle:en {#2} {#3} }
}
\cs_new_protected:Npn \mypackage_newstyle:nn #1#2
{
\keys_if_exist:nnTF { mypackage } { mypackage@#1 }
{ \msg_error:nnn { mypackage } { style-exists } {#1} }
{ \keys_define:nn { mypackage } { mypackage@#1 .meta:n = {#2} } }
}
\msg_new:nnn { mypackage } { style-exists }
{ The~ style~ `#1'~ already~ exists.~ Ignoring~ new~ definition. }
\cs_generate_variant:Nn \mypackage_newstyle:nn { en, ee }
% \setstyle{style}: set the provided style for the given package
\NewDocumentCommand \setstyle { m } { \mypackage_setstyle:e {#1} }
\cs_new_protected:Npn \mypackage_setstyle:n #1
{ \keys_set:nn { mypackage } { mypackage@#1 } }
\cs_generate_variant:Nn \mypackage_setstyle:n { e }
% \mypackagereset: reset options to default
% initial style (speed optimised, this isn't done via key=value at use time)
\cs_generate_variant:Nn \keys_precompile:nnN { ne }
\keys_precompile:neN { mypackage }
{
option = \exp_not:V \l_mypackage_option_tl
,eoption = \exp_not:V \l_mypackage_eoption_tl
,color = \exp_not:V \l_mypackage_color_tl
,choice = \exp_not:V \l_mypackage_choice_tl
}
\l_tmpa_tl
\exp_args:NNnV \NewDocumentCommand \mypackagereset {} \l_tmpa_tl
% closing
\end{filecontents}
\documentclass[varwidth = true]{standalone}
\usepackage[blue]{\jobname}
\newstyle{mystyle}{option = A, eoption = B, green, choice = false}
\begin{document}
\print{1}
\mypackagesetup{red}
\print{2}
\mypackagesetup{choice}
\print{3}
\mypackagereset
\print{4}
\begingroup
\mypackagesetup{option = X, eoption = Y}
\print{5}
\endgroup
\print{6}
\setstyle{mystyle}
\print{7}
\end{document}
以下是 中的所有密钥处理程序pgfkeys
(如 3.1.10 版第 87.4 节所述pgfmanual
)及其在l3keys
和ltkeys
层中的对应项(如您所见,它要小得多,但在里面\DeclareKeys
您也可以使用列中的所有处理程序l3keys
)。此外,我还包含了一个包含expkv-def
类型的列(因为我是 的作者expkv
,这是一个无耻的广告)。
请记住,它的l3keys
灵感来自pgfkeys
,但有一些非常不同的设计选择(例如,定义和设置的分离),因此列表看起来相当稀疏。但是,l3keys
这个表中缺少 中定义的相当多的处理程序,因为它们在 中没有对应项pgfkeys
。
pgfkeys |
l3keys |
ltkeys |
expkv-def |
---|---|---|---|
.cd |
不适用 | 不适用 | 不适用 |
.is family |
不适用l1 | 不适用 | 不适用e1 |
.default |
.default:n |
不适用 | default |
.value required |
.value_required:n = true |
不适用 | 内置e2 |
.value forbidden |
.value_forbidden:n = true |
不适用 | 内置e2 |
.code |
.code:n |
.code |
code /noval |
.ecode |
不适用 | 不适用 | ecode /enoval |
.code 2 args |
不适用 | 不适用 | 不适用 |
.ecode 2 args |
不适用 | 不适用 | 不适用 |
.code n args |
不适用 | 不适用 | 不适用 |
.ecode n args |
不适用 | 不适用 | 不适用 |
.code args |
不适用 | 不适用 | 不适用 |
.ecode args |
不适用 | 不适用 | 不适用 |
.add code |
不适用 | 不适用 | 不适用 |
.prefix code |
不适用 | 不适用 | 不适用 |
.append code |
不适用 | 不适用 | also code /also noval |
.style |
.meta:n |
不适用 | meta /nmeta |
.estyle |
不适用 | 不适用 | exp -符号 +meta |
.style 2 args |
不适用 | 不适用 | 不适用 |
.estyle 2 args |
不适用 | 不适用 | 不适用 |
.style n args |
不适用 | 不适用 | 不适用 |
.add style |
不适用 | 不适用 | 不适用 |
.style args |
不适用 | 不适用 | 不适用 |
.estyle args |
不适用 | 不适用 | 不适用 |
.prefix style |
不适用 | 不适用 | 不适用 |
.append style |
不适用 | 不适用 | also meta |
.initial |
不适用l2 | 不适用 | 不适用e3 |
.get |
不适用 | 不适用 | 不适用 |
.add |
不适用 | 不适用 | 不适用 |
.prefix |
不适用 | 不适用 | 不适用 |
.append |
不适用 | 不适用 | 不适用 |
.link |
不适用 | 不适用 | 不适用 |
.store in |
.tl_set:N |
.store |
store |
.estore in |
.tl_set_e:N |
不适用 | estore |
.is if |
.legacy_if_set:n |
.if |
bool |
.is choice |
.choice: /.choices:nn |
不适用 | choice |
.expand once |
不适用 | 不适用 | exp -符号 |
.expand twice |
不适用 | 不适用 | exp -符号 |
.expanded |
不适用 | 不适用 | exp -符号 |
.evaluated |
不适用 | 不适用 | 不适用 |
.list |
不适用l3 | 不适用 | 不适用 |
.forward to |
不适用 | 不适用 | also meta |
.search also |
不适用 | 不适用 | unknown redirect |
.try |
不适用l4 | 不适用 | 不适用e4 |
.retry |
不适用 | 不适用 | 不适用 |
.lastretry |
不适用 | 不适用 | 不适用 |
.show value |
不适用 | 不适用 | 不适用 |
.show code |
不适用 | 不适用 | 不适用 |
可以使用l1.groups:n
键进行过滤,但.cd
不支持该功能的 -part。
l2
提供.initial:n
的l3keys
是不可比的,那个是用来设置值的(因为定义和设置键是分离的)。
l3
为选择类似键,有
.multichoice:
/ .multichoices:nn
。
l4
您可以使用 检查密钥是否存在,\keys_if_exists:nnTF
但没有.try
类似的处理程序。此外,您还可以使用
\keys_set_known:nnN
仅设置已知密钥(例如.try
一次设置所有密钥)。
e1
该.cd
部分可以使用set
,没有内置按键过滤expkv
,只有设置。
e2
内置行为,因为expkv
区分带有值的键和不带有值的键(并且两者可能共享相同的用户级名称)。
e3
就像l3keys
,定义和设置键是解耦的,
initial
类型设置一个值,并且不能与 进行
.initial
比较pgfkeys
。
e4\ekvifdefined
您可以使用和 检查密钥是否存在,但在使用时
\ekvifdefinedNoVal
没有类似的处理程序。.try