8

I’ve created a simple package with options settable via l3keys2e. I was able to figure out how to make the syntax

\usepackage[format = international]{phone}

work, but I’d like to make the syntax

\usepackage[international]{phone}

be a synonym. The method I’m using in the code below works, but is very repetitive, especially when I’ve got more formatting options. Is there a simpler way?

Here’s the code:

\RequirePackage{expl3, l3keys2e, xparse}
\ProvidesExplPackage
    {phone}{2013/06/19}{0.1}{Format phone numbers}

\cs_new_nopar:Npn \phone_international_fmt:NNN  #1#2#3
  { +1 - #1 - #2 - #3 }

\cs_new_nopar:Npn \phone_parendash_fmt:NNN  #1#2#3
  { (#1) \nobreakspace #2 - #3 }

\keys_define:nn { phone }
  {
    format .choice_code:n =
      {
        \cs_new_eq:Nc \phone_fmt:NNN 
          { phone_ \tl_use:N \l_keys_choice_tl _fmt:NNN }
      },
    format .generate_choices:n =
      {
        international,
        parendash,
      },
% These next lines will become repetitive if I have many more formatting options
    international .meta:n = { format = international },
    parendash     .meta:n = { format = parendash     },
  }

\ProcessKeysOptions { phone }

\NewDocumentCommand \phone
  { >{ \SplitArgument {2} {-} } m }
  { \phone_fmt:NNN #1 }

\endinput

A minimal example using this package looks like this:

\documentclass{article}

% \usepackage[parendash]{phone}
\usepackage[format = parendash]{phone}

\begin{document}

\phone{212-555-1212}

\end{document}

This yields the desired result

(212) 555-1212

  • I’m not at all certain I’ve got the nomenclature correct; feel free to rename this question to fix this. – J. C. Salomon Jun 19 '13 at 23:23
  • Aside: \cs_new_nopar:Npn \phone_international_fmt:NNN isn't really encouraged, and I'd suggest \cs_new:Npn \phone_international_fmt:NNN instead. Apart from a few specific internals, we've standardised on a position that functions with arguments should be 'long'. – Joseph Wright Jun 20 '13 at 7:46
  • Shouldn't the :NNN argument spec rather be :nnn? – clemens Jun 20 '13 at 8:04
  • @cgnieder Yes; in that code all the :NNN should be :nnn – egreg Jun 20 '13 at 8:55
  • @JosephWright, is that the case, even though xparse defaults to short arguments? – J. C. Salomon Jun 20 '13 at 14:46
6

You could use an unknown key handler for this whereby you pass any unknown key back into the system as format = <unknown key> (a similar system is used by TikZ to recognise colours and node shapes). Here's an example which uses that. It gives a warning if there isn't a corresponding format.

I have to use a helper command to feed the unknown key back into the key handler since if I just did format / unknown .code:n = { \keys_set:nn { phone} { format = #1 } } then the #1 would be \l_keys_key_tl and L3 would be extremely careful and not expand the \l_keys_key_tl until the last minute, by which time it would have changed its value from the unknown key to format. The helper macro allows me to use the value of \l_keys_key_tl instead of the token list variable.

I also fixed the NNN to nnn. Because I wanted to set a default, the setter inside the key handler is now \cs_set_eq:Nc instead of \cs_new_eq:Nc.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/120102/86}
\usepackage{filecontents}

\begin{filecontents}{phone.sty}
\RequirePackage{expl3, l3keys2e, xparse}
\ProvidesExplPackage
    {phone}{2013/06/19}{0.1}{Format phone numbers}

\cs_new_nopar:Npn \phone_international_fmt:nnn  #1#2#3
  { +1 - #1 - #2 - #3 }

\cs_new_nopar:Npn \phone_parendash_fmt:nnn  #1#2#3
  { (#1) \nobreakspace #2 - #3 }

% Set a default
\cs_set_eq:NN \phone_fmt:nnn \phone_parendash_fmt:nnn

\msg_new:nnnn {phone} {no~ format} {Format~ `#1'~ is~ not~ recognised~ by~ this~ package,~ using~ the~ default~ `parendash'~ instead.} {}

\keys_define:nn { phone }
  {
    format .choice:,
    format .choice_code:n =
      {
        \cs_set_eq:Nc \phone_fmt:nnn 
        { phone_ \tl_use:N \l_keys_choice_tl _fmt:nnn }
      },
    format .generate_choices:n =
      {
        international,
        parendash,
      },
      format / unknown .code:n = {
        \msg_warning:nnn {phone} {no~ format} {#1}
      },
    unknown .code:n = {
      \phone_unknown_key:V \l_keys_key_tl
    },
  }

\cs_new:Npn \phone_unknown_key:n #1
{
  \keys_set:nn { phone} { format = #1 }
}

\cs_generate_variant:Nn \phone_unknown_key:n {V}


\ProcessKeysOptions { phone }

\NewDocumentCommand \phone
  { >{ \SplitArgument {2} {-} } m }
  { \phone_fmt:nnn #1 }

\endinput
\end{filecontents}
 \usepackage[british]{phone}

\begin{document}

\phone{212-555-1212}

\end{document}
  • This answers the question I asked, hence the upvote and acceptance. As it happens, the actual package has multiple keys for which I want the values as shortcuts, so I’ll actually be using Joseph’s solution. – J. C. Salomon Jun 20 '13 at 14:51
7

The approach you've taken looks correct. Internally, the .meta:n property is creating a standard key which is more-or-less equivalent to

key-name-1 .code:n = { \keys_set:nn { module } { key-name-2 = #1 } }

If you have a large number of these meta keys to create, you might want to use a comma-list mapping

\clist_map_inline:nn { key-one , key-two , key-three ... }
  { \keys_define:nn { mymodule } { #1 .meta:n = { format = #1 } }

but for just a couple this is probably making your code more not less complex.

  • Considering that I’ll also be adding .value_forbidden: for each of these shortcuts, the benefit starts to outweigh the cost very quickly. – J. C. Salomon Jun 20 '13 at 14:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.