我想定义一个函数\keys_filter:nnnN
,它可以做与设置完全相同的事情,\keys_set_filter:nnnN
但没有设置。我意识到设置是由核心函数实现的\__keys_set_elt_aux:
,如果我在本地禁用它,一切都会正常工作。(LaTeX3 警察正在追捕我。我知道。)但是,我修改了核心,这是不被接受的。有没有更干净的方法来实现这一点?
下面我将展示一个示例,说明如何使用此功能分离经过筛选属于不同密钥组的密钥。
\documentclass{article}
\usepackage{expl3}[2014/11/25]
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Nn \my_keys_filter:nnnN {
\group_begin:
% Disable the L3 core fucntion responsible for setting the key
\cs_set_eq:NN \__keys_set_elt_aux: \prg_do_nothing:
% Use \keys_set_filter
\keys_set_filter:nnnN { #1 } { #2 } { #3 } \l_my_temp_tl
% Globalize the token list
\tl_gset_eq:NN \g_my_temp_tl \l_my_temp_tl
\group_end:
% Assign the token list to the output
\tl_set_eq:NN #4 \g_my_temp_tl
}
\cs_generate_variant:Nn \my_keys_filter:nnnN { nnV }
\keys_define:nn { my } {
% group A key
one .code:n = { (ONE:#1) },
one .groups:n = { grpA },
% group A+B key
two .code:n = { (TWO:#1) },
two .groups:n = { grpAB },
% group A+B+C key
three .code:n = { (THREE:#1) },
three .groups:n = { grpABC },
}
\cs_generate_variant:Nn \msg_error:nnn { nnV }
% Just messages
\msg_new:nnn { my } { keys-only-A } { The~key~'#1'~can~appear~only~in~\token_to_str:N \takesABC . }
\msg_new:nnn { my } { keys-only-AB } { The~key~'#1'~can't~appear~in~\token_to_str:N \takesC . }
% If a key falls in my-only-A(B) key module, we call the error function
\keys_define:nn { my-only-A } { unknown .code:n = { \msg_error:nnV { my } { keys-only-A } \l_keys_key_tl } }
\keys_define:nn { my-only-AB } { unknown .code:n = { \msg_error:nnV { my } { keys-only-AB } \l_keys_key_tl } }
% Takes anything
\DeclareDocumentCommand \takesABC { O{} } {
\keys_set:nn { my } { #1 }
...
}
% Doesn't take A keys
\DeclareDocumentCommand \takesBC { O{} } {
\keys_set_filter:nnnN { my } { grpA } { #1 } \l_my_filtered_keys_tl
\keys_set:nV { my-only-A } \l_my_filtered_keys_tl
...
}
% Takes only C keys
\DeclareDocumentCommand \takesC { O{} } {
\keys_set_filter:nnnN { my } { grpA, grpAB } { #1 } \l_my_filtered_keys_tl
\my_keys_filter:nnVN { my } { grpA } \l_my_filtered_keys_tl \l_my_filtered_B_keys_tl
\my_keys_filter:nnVN { my } { grpAB } \l_my_filtered_keys_tl \l_my_filtered_C_keys_tl
\keys_set:nV { my-only-A } \l_my_filtered_B_keys_tl
\keys_set:nV { my-only-AB } \l_my_filtered_C_keys_tl
...
}
\ExplSyntaxOff
\begin{document}
\takesABC[one = 11, two = 22, three = 33]
\takesBC[two = 222, three = 333]
\takesC[three = 3333]
These generate errors:
\takesBC[one = 1, two = 2, three = 3]
\takesC[one = I, two = II, three = III]
\end{document}
我实际上想做什么?我有一个类的 KV 接口,并且有 3 个地方可以设置键:
\documentclass[one=1]{myclass}
\mysetup{ two=2 }
\myprocessthings
\mysetup{ three=3 }
\begin{document}
...
\end{document}
并且有些键在某些“后续上下文”中没有用,例如,当调用类时,字体大小是固定的,而 babel 是在 中调用的\myprocessthings
,因此使用的语言在那里是固定的。我可能会犯一个一般性错误Key '#1' used later than where it is allowed
,但我想更准确地表达我的意思,为此,我需要区分这些键。(很抱歉,这个的 MWE 会更长,因为它需要一个类文件,所以我决定让它更简单,可能太多了。)
答案1
密钥过滤适用于相当特殊的情况,即多个密钥有意义但需要额外的处理或排序。特别是,它并不是处理密钥的正确方法,因为从某个固定点开始,它们不再有意义。例如,许多软件包都有只影响整个文档的前言选项:这些密钥不需要过滤,它们需要禁用!例如,可以使用钩子来\AtBeginDocument
处理
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\keys_define:nn { mymodule }
{
key-one .tl_set:N = \l_my_a_tl ,
key-two .tl_set:N = \l_my_b_tl
}
\AtBeginDocument
{
\keys_define:nn { mymodule }
{
key-one .code:n = \msg_error:nnn
{ mymodule } { key-preamble-only } { key-one }
}
}
\msg_new:nnn { mymodule } { key-preamble-only }
{ Key~'#1'~only~valid~in~the~preamble! }
\cs_new_eq:NN \SetKeys \keys_set:nn
\ExplSyntaxOff
\SetKeys{mymodule}{key-one = value-a, key-two = value-b}
\begin{document}
\SetKeys{mymodule}{key-one = value-c, key-two = value-d}
\end{document}
如果有两组键在两个文档命令中具有并行用法,并且在第三个文档命令中具有通用用法,则可以使用键分组
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\keys_define:nn { mymodule }
{
key-one .tl_set:N = \l_my_a_tl ,
key-one .groups:n = a,
key-two .tl_set:N = \l_my_b_tl,
key-two .groups:n = b
}
\cs_new_eq:NN \SetKeys \keys_set:nn
\cs_new_eq:NN \SetGroupKeys \keys_set_groups:nnn
\ExplSyntaxOff
\begin{document}
\SetGroupKeys{mymodule}{a}{key-one = value-a, key-two = value-b}
\SetGroupKeys{mymodule}{b}{key-one = value-c, key-two = value-d}
\SetKeys{mymodule}{key-one = value-e, key-two = value-f}
\end{document}
或者使用两个子模块和多遍方法,例如
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\keys_define:nn { mymodule }
{
a / key-one .tl_set:N = \l_my_a_tl ,
b / key-two .tl_set:N = \l_my_b_tl
}
\cs_new_eq:NN \SetKeys \keys_set:nn
\cs_new_eq:NN \SetKnownKeys \keys_set_known:nn
\ExplSyntaxOff
\begin{document}
\SetKnownKeys{mymodule / a}{key-one = value-a, key-two = value-b}
\SetKnownKeys{mymodule / b}{key-one = value-c, key-two = value-d}
\end{document}