LATEX-L Archives

Mailing list for the LaTeX3 project

LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

Options: Use Classic View

Use Monospaced Font
Show Text Part by Default
Show All Mail Headers

Topic: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
Joseph Wright <[log in to unmask]>
Sat, 13 Jul 2013 10:38:57 +0100
text/plain (209 lines)
Hello Jura,

>> The above interface leaves open a few questions:
>>
>>  - How is nesting handled? Does \keys_set:nn within
>>    \keys_set_grouped:nnn respect groups? Does
>>    \keys_set_grouped:nnn within \keys_set_grouped:nnn
>>    work in a union or intersection way?
> 
> I'd say, 'yes' to the first (that would be closest to what is done in
> pgfkeys), and given that, 'intersection' to the second.

I've been looking at this in more detail, and I suspect that sticking to
the 'no nesting' position is easiest (other than \keys_set:nn within
\keys_set_... respects the outer position: needed for .meta:n, etc.). I
don't see a lot a real use cases for nesting, while I do see quite a bit
of complexity in allowing it!

>>  - Do unknown keys raise an error, or are they ignored as they
>>    are not in any group?
> 
> Following pgfkeys again, I think they should raise an error.

See below.

>>  - Do unused options need to be collected/available?
> 
> I've not had the occasion to use this myself, but pgfkeys does provide
> it. One application that immediately pops to my mind is the control of
> key-setting order when the order is important, which is potentially
> rather useful. So why not have \keys_set_grouped:nnnN, allowing
> something like:

OK, I can build in the ability to do this. Worth bearing in mind that
grouping is intended for the case where you want to have a 'second axis'
for keys. If you always want to be able to set stuff in an order, then
\keys_set_known:nnN plus subtrees already works. (We added this a while
ago, primarily as Will needed it for fontspec.)

> \cs_new_protected:Npn \__mymodule_keys_set_in_order_and_do_something:n #1

> Another question is what should happen to keys that haven't been
> assigned a group. Right now, they're filtered out, but perhaps it
> would be better to set them. In that way, if there are keys that
> should never be filtered, they won't need to be assigned a fallback
> group to accomplish this. Pgfkeys provides both options, but it seems
> to me it's easier for package writers using l3keys to just follow the
> principle that they should assign a key to a group only if they ever
> want to filter it out. This ties in with one aspect of how the draft
> \keys_set_groups:nnn behaves that I feel should be definitely changed.
> Right now, when the second argument is empty, all the keys get set,
> which is the opposite of what one would expect. I think, instead, no
> keys should be set, except for those that have no group assigned to
> them. I believe changing \tl_set:Nx \l__keys_groups_clist {#4} to
> \tl_set:Nx \l__keys_groups_clist { , #4 } in the definition of
> \__keys_set_grouped:nnnnn would do this.

I get the feeling there are two related but distinct requirements here:

 - Set only keys from specified groups ('opt-in')
 - Set keys unless they are in groups specified to be filtered out
   ('opt-out')

The following implements that, but note that you will need the 'burning
edge' expl3 from GitHub/SVN as I've made some adjustments to the l3keys
code to allow for things we certainly will want whatever the final form
of groups/families.

\cs_new_protected:Npn \__keys_groups_set:n #1
  {
    \prop_if_exist:cT { \c__keys_info_root_tl \l_keys_path_tl }
      {
        \prop_put:cnn { \c__keys_info_root_tl \l_keys_path_tl }
          { groups } {#1}
      }
  }
\cs_new_protected:cpn { \c__keys_props_root_tl .groups:n } #1
  { \__keys_groups_set:n {#1} }

\bool_new:N \l__keys_filter_bool
\bool_new:N \l__keys_groups_bool
\seq_new:N \l__keys_groups_seq
\tl_new:N \l__keys_groups_tl
\bool_new:N \l__keys_tmp_bool

\cs_new_protected:Npn \keys_set_filter:nnn #1#2#3
  {
    \bool_set_true:N \l__keys_filter_bool
    \seq_set_from_clist:Nn \l__keys_groups_seq {#2}
    \keys_set:nn {#1} {#3}
    \bool_set_false:N \l__keys_filter_bool
  }
\cs_new_protected:Npn \keys_set_filter:nnnN #1#2#3#4
  {
    \clist_clear:N \l__keys_unused_clist
    \keys_set_filter:nn {#1} {#2} {#3}
    \tl_set:Nx #3 { \exp_not:o { \l__keys_unused_clist } }
  }
\cs_new_protected:Npn \keys_set_groups:nnn #1#2#3
  {
    \bool_set_true:N \l__keys_groups_bool
    \seq_set_from_clist:Nn \l__keys_groups_seq {#2}
    \keys_set:nn {#1} {#3}
    \bool_set_false:N \l__keys_groups_bool
  }

\cs_set_protected:Npn \__keys_set_elt_aux:nn #1#2
  {
    \tl_set:Nx \l_keys_key_tl { \tl_to_str:n {#1} }
    \tl_set:Nx \l_keys_path_tl { \l__keys_module_tl / \l_keys_key_tl }
    \__keys_value_or_default:n {#2}
    \bool_if:nTF { \l__keys_filter_bool || \l__keys_groups_bool }
      { \__keys_set_elt_grouped: }
      { \__keys_set_elt_aux: }
  }
\cs_new_protected_nopar:Npn \__keys_set_elt_aux:
  {
    \bool_if:nTF
      {
        \__keys_if_value_p:n { required } &&
        \l__keys_no_value_bool
      }
      {
        \__msg_kernel_error:nnx { kernel } { value-required }
          { \l_keys_path_tl }
      }
      {
        \bool_if:nTF
          {
              \__keys_if_value_p:n { forbidden } &&
            ! \l__keys_no_value_bool
          }
          {
            \__msg_kernel_error:nnxx { kernel } { value-forbidden }
              { \l_keys_path_tl } { \l_keys_value_tl }
          }
          { \__keys_execute: }
      }
  }
\cs_new_protected_nopar:Npn \__keys_set_elt_grouped:
  {
    \prop_if_exist:cTF { \c__keys_info_root_tl \l_keys_path_tl }
      {
        \prop_get:cnNTF { \c__keys_info_root_tl \l_keys_path_tl }
          { groups } \l__keys_groups_tl
          { \__keys_check_groups: }
          {
            \bool_if:NTF \l__keys_filter_bool
              { \__keys_set_elt_aux: }
              { \__keys_store_unused: }
          }
      }
      {
        \bool_if:NTF \l__keys_filter_bool
          { \__keys_set_elt_aux: }
          { \__keys_store_unused: }
      }
  }
\cs_new_protected_nopar:Npn \__keys_store_unused:
  {
    \clist_put_right:Nx \l__keys_unknown_clist
      {
        \exp_not:o \l_keys_key_tl
        \bool_if:NF \l__keys_no_value_bool
          { = { \exp_not:o \l_keys_value_tl } }
      }
  }
\cs_new_protected_nopar:Npn \__keys_check_groups:
  {
    \bool_set_false:N \l__keys_tmp_bool
    \seq_map_inline:Nn \l__keys_groups_seq
      {
        \clist_map_inline:Vn \l__keys_groups_tl
          {
            \str_if_eq:nnT {##1} {####1}
              {
                \bool_set_true:N \l__keys_tmp_bool
                \clist_map_break:n { \seq_map_break: }
              }
          }
      }
    \bool_if:NTF \l__keys_tmp_bool
      {
        \bool_if:NTF \l__keys_filter_bool
          { \__keys_store_unused: }
          { \__keys_set_elt_aux: }
      }
      {
        \bool_if:NTF \l__keys_filter_bool
          { \__keys_set_elt_aux: }
          { \__keys_store_unused: }
      }
  }
\cs_generate_variant:Nn \clist_map_inline:nn { V }


\keys_define:nn { module }
  {
    key-one .code:n   = { \tl_show:n { key-one } },
    key-one .groups:n = { a , b },
    key-two .code:n   = { \tl_show:n { SAW:~key-two } },
    key-two .groups:n = { c , f },
  }

\keys_set_groups:nnn { module } { a , c }
  { key-one = value , key-two = value-test , key-odd = value }
-- 
Joseph Wright

ATOM RSS1 RSS2