TransWikia.com

keyval: pass unknown options to a command

TeX - LaTeX Asked by T. Pluess on May 4, 2021

Consider the following MWE:

documentclass[a4paper]{article}
usepackage{keyval}

makeatletter
define@key{my}{foo}[]{foo is enabledpar}
define@key{my}{bar}[0]{bar is set to #1par}
makeatother

newcommand{somecommand}[2][none]{arg 1: #1, arg 2: #2}
newcommand{othercommand}[2][]{setkeys{my}{#1} somecommand{#2}}

begin{document}

othercommand[foo,bar=9]{baz}

end{document}

This works fine as long as the only optional arguments passed to othercommand are either foo or bar. Since these are optional arguments, they may be omitted as well. But what I would like to do now is the following: if there is another optional argument given, not known to the othercommand, it shall be passed to the somecommand. Example: if I write

othercommand[foo, bar=29, zap=12]{baz}

I want the options foo and bar still be processed by the othercommand. But the zap=12 option is not known, so it shall be passed to somecommand without modification, such that I can write

newcommand{othercommand}[2][]{setkeys{my}{#1} somecommand[pass unknown options here]{#2}}

For documentclasses, there is a command like “pass options to class” which passes unknown documentclass options to the underlying document class. I wonder whether such a mechanism can be used here as well.

2 Answers

The following is an example how this could be done with expkv. Note that the unknown key isn't forwarded exactly like it was passed in (that information is lost when expkv has parsed the current key=val pair), but in a way in which most key=value packages would parse an equivalent value of pair (I say most because some of the wide spread packages have some issues regarding brace stripping, most notably it is possible that pgfkeys doesn't parse correctly something which is passed in as key= {value}).

documentclass[]{article}

usepackage{expkv}

makeatletter
ekvdefNoVal{my}{foo}{foo is enabledpar}
ekvdef{my}{bar}{bar is set to #1par}
ekvdefNoVal{my}{bar}{bar is set to 0par}
newcommandpluess@add@to@list[2]
  {%
    ifx@empty#1%
      def#1{#2}%
    else
      edef#1{unexpandedexpandafter{#1,#2}}%
    fi
  }
ekvdefunknownNoVal{my}{pluess@add@to@listpluess@my@unknown@list{#1}}
ekvdefunknown{my}{pluess@add@to@listpluess@my@unknown@list{#2= {#1}}}
newcommand*pluess@my@unknown@list{}
ekvsetdefmy@set{my}

newcommandothercommand[2][]
  {%
    begingroup
      letpluess@my@unknown@list@empty
      my@set{#1}%
      expandaftersomecommandexpandafter
        [expandafter{pluess@my@unknown@list}]%
        {#2}%
    endgroup
  }
newcommandsomecommand[2][none]{arg 1: #1, arg 2: #2par}
makeatother

begin{document}
othercommand{baz}
othercommand[foo, bar=9]{baz}
othercommand[foo, bar=9, zip=12]{baz}
end{document}

If you want to only use the optional argument of somecommand if there were any unknown keys and else stick to its default, you could use:

documentclass[]{article}

usepackage{expkv}

makeatletter
ekvdefNoVal{my}{foo}{foo is enabledpar}
ekvdef{my}{bar}{bar is set to #1par}
ekvdefNoVal{my}{bar}{bar is set to 0par}
newcommandpluess@add@to@list[2]
  {%
    ifx@empty#1%
      def#1{#2}%
    else
      edef#1{unexpandedexpandafter{#1,#2}}%
    fi
  }
ekvdefunknownNoVal{my}{pluess@add@to@listpluess@my@unknown@list{#1}}
ekvdefunknown{my}{pluess@add@to@listpluess@my@unknown@list{#2= {#1}}}
newcommand*pluess@my@unknown@list{}
ekvsetdefmy@set{my}

newcommandothercommand[2][]
  {%
    begingroup
      letpluess@my@unknown@list@empty
      my@set{#1}%
      ifxpluess@my@unknown@list@empty
        expandafter@firstoftwo
      else
        expandafter@secondoftwo
      fi
      {somecommand}%
      {%
        expandaftersomecommandexpandafter
          [expandafter{pluess@my@unknown@list}]%
      }%
      {#2}%
    endgroup
  }
newcommandsomecommand[2][none]{arg 1: #1, arg 2: #2par}
makeatother

begin{document}
othercommand{baz}
othercommand[foo, bar=9]{baz}
othercommand[foo, bar=9, zip=12]{baz}
end{document}

Answered by Skillmon on May 4, 2021

The l3keys module of LaTeX3 has the function keys_set_known:nnN which stores every unknown key in a token list.

You could use it in the following way:

documentclass[]{article}

% Inside of ExplSyntaxOn ... ExplSyntaxOff spaces are ignored and you have to
% use ~ to insert a space instead. Also _ and : can be part of macro names which
% is used to add some structure.
ExplSyntaxOn
keys_define:nn { my }
  {
     foo .code:n = { foo ~ is ~ enabled par }
    ,foo .value_forbidden:n = { true }
    ,bar .code:n = { bar ~ is ~ set ~ to ~ #1par }
    ,bar .default:n = { 0 }
  }
tl_new:N l__my_unknown_keys_tl
NewDocumentCommand othercommand { O{} m }
  {
    group_begin:
      keys_set_known:nnN { my } {#1} l__my_unknown_keys_tl
      exp_args:NNo somecommand [ l__my_unknown_keys_tl ] {#2}
    group_end:
  }
NewDocumentCommand somecommand { O{none} m }
  {
    arg ~ 1: ~ #1, ~ arg ~ 2: ~ #2par
  }
ExplSyntaxOff

begin{document}
othercommand{baz}
othercommand[foo, bar=9]{baz}
othercommand[foo, bar=9, zip=12]{baz}
end{document}

If you want to only use the optional argument of somecommand if there were any unknown keys and else stick to its default, you could use:

documentclass[]{article}

% Inside of ExplSyntaxOn ... ExplSyntaxOff spaces are ignored and you have to
% use ~ to insert a space instead. Also _ and : can be part of macro names which
% is used to add some structure.
ExplSyntaxOn
keys_define:nn { my }
  {
     foo .code:n = { foo ~ is ~ enabled par }
    ,foo .value_forbidden:n = { true }
    ,bar .code:n = { bar ~ is ~ set ~ to ~ #1par }
    ,bar .default:n = { 0 }
  }
tl_new:N l__my_unknown_keys_tl
NewDocumentCommand othercommand { O{} m }
  {
    group_begin:
      keys_set_known:nnN { my } {#1} l__my_unknown_keys_tl
      tl_if_empty:NTF l__my_unknown_keys_tl
        { somecommand {#2} }
        { exp_args:NNo somecommand [ l__my_unknown_keys_tl ] {#2} }
    group_end:
  }
NewDocumentCommand somecommand { O{none} m }
  {
    arg ~ 1: ~ #1, ~ arg ~ 2: ~ #2par
  }
ExplSyntaxOff

begin{document}
othercommand{baz}
othercommand[foo, bar=9]{baz}
othercommand[foo, bar=9, zip=12]{baz}
end{document}

Answered by Skillmon on May 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP