LATEX-L Archives

Mailing list for the LaTeX3 project

LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

Options: Use Forum View

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

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

Print Reply
Subject:
From:
Jura Pintar <[log in to unmask]>
Reply To:
Mailing list for the LaTeX3 project <[log in to unmask]>
Date:
Wed, 10 Jul 2013 12:15:57 -0400
Content-Type:
text/plain
Parts/Attachments:
text/plain (300 lines)
> Looking at this, both Bruno and I noticed that if you are allowing a
> clist then in principal you could simply extend \keys_set:nn to have a
> list for the first argument. However, I'm not sure that's quite right.
>
> If you look at the pgfkeys demo above, what you see is that all of the
> keys are in the same path:
>
>   /mymodule/key-A-i
>   /mymodule/key-B-i
>
> with the family as a 'secondary path' (or something like that). This
> means three things:
>
>  1) With filtering turned 'off', the keys can all be set in one
>     operation (no need to try different places)
>
>  2) The key names are unique
>
>  3) If the key is not found, there is one place to look for an
>     unknown handler: /mymodule/unknown
>
> On the other hand, the proposed extension to l3keys uses key path for
> filtering, so we find in contrast
>
>  1) The keys can only all be set by actively choosing each subgroup
>
>  2) Key names may not be unique, so the order of subgroups becomes
>     important
>
>  3) Handling for unknown keys is far from clear (do we look in
>     /mymodule/, /mymodule/subgroup-A/, ...?)
>
> I'm going to take a look at how pgfkeys actually handles this.
> --
> Joseph Wright


Yes, you're right, of course! The pgfkeys family structure is
independent of the tree structure, and I can't remember now why I
tried doing it based on the paths... The problems you bring up really
should have been obvious to me. :S

Anyhow, here is a version (appended below) that does follow the
pgfkeys approach more closely. I've not tested it very thoroughly, but
it seems to work... Again, I'm sure there's much room for improvement!

There are two new properties:

1) .filter:     - defines a key as a filter
2) .filters:n  - takes a clist argument that specifies which filters
are to be applied to a key (this is unlike the pgfkeys '.belongs to
family' handler, which only accepts a single family key)

And there are several new public commands:

1) \keys_filters_activate:nn  {<module>} {<filter clist>}
2) \keys_filters_deactivate:nn  {<module>} {<filter clist>}
3) \keys_filters_deactivate_all:
4) \keys_set_filtered:nn {<module>} {<keyval list>}
5) \keys_set_known_filtered:nnN {<module>} {<keyval list>} <clist>

and a couple of public variables

\l_keys_filtered_seq  - which contains the keys that were filtered out
on the last call of \keys_set_filtered:nn
\l_active_filters_seq  - which contains the filter keys currently activated

A key will be filtered out if any of the filters that apply to it are
active, and keys with no filters applied will be set normally.

\bool_new:N  \l__key_filtered_bool
\clist_new:N \l__key_filters_clist
\prop_new:N  \l__filter_states_prop
\prop_new:N  \l__keys_filters_prop
\seq_new:N   \l_keys_filtered_seq
\seq_new:N   \l_active_filters_seq
\seq_new:N   \l__active_filters_seq

\cs_generate_variant:Nn \__msg_kernel_error:nnnnn { nnoVV }
\cs_generate_variant:Nn \keys_define:nn           { Vo }
\cs_generate_variant:Nn \keys_if_exist:nnTF       { VnTF }
\cs_generate_variant:Nn \keys_set:nn              { Vn }
\cs_generate_variant:Nn \keys_set_known:nnN       { VnN }
\cs_generate_variant:Nn \prop_get:NnN             { NxN }
\cs_generate_variant:Nn \tl_if_eq:nnT             { VnT }

\cs_new_protected:Npn \__keys_filter_define:
  {
    \prop_if_in:NnF \l__filter_states_prop { \l_keys_path_tl }
      { \prop_put:NVn \l__filter_states_prop \l_keys_path_tl { 0 } }
  }
\cs_new_protected:Npn \__keys_filters_set:n #1
  {
    \clist_clear:N \l__key_filters_clist
    \clist_map_inline:nn {#1}
      {
        \prop_if_in:NoTF \l__filter_states_prop { \l__keys_module_tl / ##1 }
          {
            \clist_put_right:No \l__key_filters_clist
              { \l__keys_module_tl / ##1 }
          }
          {
            \__msg_kernel_error:nnoVV { kernel } { invalid-filter }
              { \l__keys_module_tl / ##1 } \l_keys_path_tl \l__keys_module_tl
          }
      }
    \clist_if_empty:NF \l__valid_filters_clist
      {
        \prop_put:NVV \l__keys_filters_prop
          \l_keys_path_tl \l__key_filters_clist
      }
  }
\cs_new_protected:cpn { \c__keys_props_root_tl .filter: }
  { \__keys_filter_define: }
\cs_new_protected:cpn { \c__keys_props_root_tl .filters:n } #1
  { \__keys_filters_set:n {#1} }
\cs_new_protected:Npn \keys_filters_activate:nn #1#2
  {
    \clist_map_inline:nn {#2}
      {
        \prop_if_in:NnTF \l__filter_states_prop { #1 / ##1 }
          {
            \prop_put:Nnn \l__filter_states_prop { #1 / ##1 } { 1 }
            \seq_put_right:Nn \l__active_filters_seq { #1 / ##1 }
          }
          {
            \__msg_kernel_error:nnnn { kernel } { unknown-filter }
              { #1 / ##1 } {#1}
          }
      }
    \seq_set_eq:NN \l_active_filters_seq \l__active_filters_seq
  }
\cs_new_protected:Npn \keys_filters_deactivate:nn #1#2
  {
    \clist_map_inline:nn {#2}
      {
        \prop_if_in:NnTF \l__filter_states_prop { #1 / ##1 }
          {
            \prop_put:Nnn \l__filter_states_prop { #1 / ##1 } { 0 }
            \seq_remove_all:Nn \l__active_filters_seq { #1 / ##1 }
          }
          {
            \__msg_kernel_error:nnnn { kernel } { unknown-filter }
              { #1 / ##1 } {#1}
          }
      }
    \seq_set_eq:NN \l_active_filters_seq \l__active_filters_seq
  }
\cs_new_protected:Npn \keys_filters_deactivate_all:
  {
    \seq_map_inline:Nn \l__active_filters_seq
      { \prop_put:Nnn \l__filter_states_prop {##1} { 0 } }
    \seq_clear:N \l__active_filters_seq
    \seq_set_eq:NN \l_active_filters_seq \l__active_filters_seq
  }
\cs_new_protected:Npn \keys_set_filtered:nn #1#2
  {
    \seq_clear:N \l_keys_filtered_seq
    \tl_set:Nn \l__keys_module_tl {#1}
    \keyval_parse:NNn \__keys_set_filtered:n \__keys_set_filtered:nn {#2}
  }
\cs_new_protected:Npn \__keys_set_filtered:n #1
  {
    \prop_get:NoNTF \l__keys_filters_prop
      { \l__keys_module_tl / #1 }
      \l__key_filters_clist
      {
        \bool_set_false:N \l__key_filtered_bool
        \clist_map_inline:Nn \l__key_filters_clist
          {
            \prop_get:NnN \l__filter_states_prop {##1} \l__tmpa_tl
            \tl_if_eq:VnT \l__tmpa_tl { 1 }
              {
                \bool_set_true:N \l__key_filtered_bool
                \clist_map_break:
              }
          }
        \bool_if:NTF \l__key_filtered_bool
          { \seq_put_right:Nn \l_keys_filtered_seq {#1} }
          { \keys_set:Vn \l__keys_module_tl {#1} }
      }
      { \keys_set:Vn \l__keys_module_tl {#1} }
  }
\cs_new_protected:Npn \__keys_set_filtered:nn #1#2
  {
    \prop_get:NoNTF \l__keys_filters_prop
      { \l__keys_module_tl / #1 }
      \l__key_filters_clist
      {
        \bool_set_false:N \l__key_filtered_bool
        \clist_map_inline:Nn \l__key_filters_clist
          {
            \prop_get:NnN \l__filter_states_prop {##1} \l__tmpa_tl
            \tl_if_eq:VnT \l__tmpa_tl { 1 }
              {
                \bool_set_true:N \l__key_filtered_bool
                \clist_map_break:
              }
          }
        \bool_if:NTF \l__key_filtered_bool
          { \seq_put_right:Nn \l_keys_filtered_seq { #1 = #2 } }
          { \keys_set:Vn \l__keys_module_tl { #1 = #2 } }
      }
      { \keys_set:Vn \l__keys_module_tl { #1 = #2 } }
  }
\cs_new_protected:Npn \keys_set_known_filtered:nnN #1#2#3
  {
    \seq_clear:N \l_keys_filtered_seq
    \clist_clear:N \l__tmpa_clist
    \clist_clear:N \l__tmpb_clist
    \tl_set:Nn \l__keys_module_tl {#1}
    \keyval_parse:NNn
      \__keys_set_known_filtered:n
      \__keys_set_known_filtered:nn
      {#2}
    \clist_concat:NNN #3 \l__tmpa_clist \l__tmpb_clist
  }
\cs_new_protected:Npn \__keys_set_known_filtered:n #1
  {
    \prop_get:NoNTF \l__keys_filters_prop
      { \l__keys_module_tl / #1 }
      \l__key_filters_clist
      {
        \bool_set_false:N \l__key_filtered_bool
        \clist_map_inline:Nn \l__key_filters_clist
          {
            \prop_get:NnN \l__filter_states_prop {##1} \l__tmpa_tl
            \tl_if_eq:VnT \l__tmpa_tl { 1 }
              {
                \bool_set_true:N \l__key_filtered_bool
                \clist_map_break:
              }
          }
        \bool_if:NTF \l__key_filtered_bool
          { \seq_put_right:Nn \l_keys_filtered_seq {#1} }
          { \keys_set_known:VnN \l__keys_module_tl {#1} \l__tmpa_clist }
      }
      { \keys_set_known:VnN \l__keys_module_tl {#1} \l__tmpa_clist }
  }
\cs_new_protected:Npn \__keys_set_known_filtered:nn #1#2
  {
    \prop_get:NoNTF \l__keys_filters_prop
      { \l__keys_module_tl / #1 }
      \l__key_filters_clist
      {
        \bool_set_false:N \l__key_filtered_bool
        \clist_map_inline:Nn \l__key_filters_clist
          {
            \prop_get:NnN \l__filter_states_prop {##1} \l__tmpa_tl
            \tl_if_eq:VnT \l__tmpa_tl { 1 }
              {
                \bool_set_true:N \l__key_filtered_bool
                \clist_map_break:
              }
          }
        \bool_if:NTF \l__key_filtered_bool
          { \seq_put_right:Nn \l_keys_filtered_seq { #1 = #2 } }
          { \keys_set_known:VnN \l__keys_module_tl { #1 = #2 } \l__tmpb_clist }
      }
      { \keys_set_known:VnN \l__keys_module_tl { #1 = #2 } \l__tmpb_clist }
  }
\__msg_kernel_new:nnnn { kernel } { invalid-filter }
  { The~filter~'#1'~does~not~exist~and~will~not~be~applied~to~key~'#2'. }
  {
    Filter~’#1’~has~not~been~defined~for~the~module~'#3'.\\
    A~filter~must~be~defined~using~the~.filter:~property~
    before~it~can~be~applied~to~any~keys.
  }
\__msg_kernel_new:nnnn { kernel } { unknown-filter }
  { The~filter~'#1'~is~not~defined.~It~cannot~be~activated~or~deactivated. }
  {
    Filter~’#1’~has~not~been~defined~for~the~module~'#2'.\\
    A~filter~must~be~defined~using~the~.filter:~property~
    before~it~can~be~applied.
  }




By the way, the syntax and function of my initially proposed
\keys_subgroups_set:nnn could be roughly captured by the following
code (though I doubt it would add anything useful any more)

\cs_new_protected:Npn \keys_subgroups_set:nnn #1#2#3
  {
    \prop_set_eq:NN \l__tmpa_prop \l__filter_states_prop
    \keys_filters_deactivate_all:
    \keys_filters_activate:nn  {#1} {#2}
    \keys_set_filtered:nn {#1} {#3}
    \prop_set_eq:NN \l__filter_states_prop \l__tmpa_prop
  }
\cs_new_protected:Npn \keys_subgroups_set_known:nnn #1#2#3
  {
    \prop_set_eq:NN \l__tmpa_prop \l__filter_states_prop
    \keys_filters_deactivate_all:
    \keys_filters_activate:nn  {#1} {#2}
    \keys_set_known_filtered:nn {#1} {#3}
    \prop_set_eq:NN \l__filter_states_prop \l__tmpa_prop
  }

ATOM RSS1 RSS2