3

The pgfkeys package has the /.code handler that allows one to define the code to be executed when the user sets a key, as if it executed a macro with #1 as the value. The /.code args handler allows to provide an actual argument list in \def-style so one can define fancy keys, like rectangle/.code args={#1 by #2}{...} that can be set as rectangle=13 by 12.

My question is: is there some package that integrates xparse with pgfkeys in order to use xparse-like syntax (e.g. {m o m}) to define the arguments of the key?

Example:

\pgfkeys{my key/.xparse code={o m}{\def\mykey{Required: #2, Optional: #1}}}
\pgfkeys{my key = [Hello]{John}} % prints 'Required: John, Optional: Hello'
1
  • I've tried a few things but since PGFkeys uses delimited arguments and o m isn't, calling my key = John might lead to only J picked up as #2. But defining the DocumentCommand delimited lead to other weird things that won't be consistent with xparse when something like omo is used as an argment definition. Commented Oct 19, 2022 at 11:42

1 Answer 1

1

Besides just defining your own DocumentCommand and just forwarding everything from PGFkeys there manually, say

\NewDocumentCommand\myKey{o m}{<stuff>}
\pgfkeys{my key/.code=\myKey#1}

we could do it with a handler that does the same.

my key/.xparse code = {o m}{<stuff>}

which can be used with

my key = [opt]{mand}

and it'll work right:

\myKey[opt]{mand}

However, when used without an optional argument, e.g.

my key = mand
% or
my key = {mand}

PGFkeys will lose some braces and the call to \myKey will be

\myKey mand

which will only grab m as #2 and leave and around.

We'd need my key = {{{{mand}}}}.


PGFkeys works in one aspect very different than \newcommands or \NewDocumentCommands: Every key-macro is a delimited macro whose arguments and with \pgfeov. Internally, all key-macros get this appended.

Unfortunately, xparse doesn't provide the same interface, it only knows

  • t → an optional token (which will toggle a Boolean) and
  • u → a mandatory argument until the specified tokens are encountered.

I've chose the latter. That means, every

key/.xparse code={<args>}{<code>}

will actually be defined with the arguments <args> u\pgfeov.

This makes it very easy to use xparse for PGFkeys with one caveat: There's always a mandatory last argument the user does not specify.

This isn't great but I would reiterate a recommendation from the xparse: Keep It Simple (paraphrasing). Just don't put any fancy arguments at the end.

In your case, you would just use o since, again, a mandatory argument at the end is mandatory.

\pgfkeys{my key/.xparse code={o}{Required: #2, Optional: #1}}

A few notes:

  • If you need .append xparse code and similars a bit more work is necessary, I have a few ideas, though.

  • I'm explicitly showing almost only bad cases in my code below don't be discouraged.

  • I'm using \DeclareDocumentCommand since this will just overwrite the macro (as PGFkeys does with .code) even though the xparse manual advices against it:

    This should be used sparingly.

Code

\documentclass{article}
\usepackage{pgfkeys,xparse,tabularx}
\pgfqkeys{/handlers}{
  .xparse code/.code 2 args={%
    \expandafter\DeclareDocumentCommand
      \csname pgfk@\pgfkeyscurrentpath/.@cmd\endcsname
      {#1u\pgfeov}{#2}%
    % probably useless unless .xparse append code is needed
    %\pgfkeyssetvalue{\pgfkeyscurrentpath/.@args}{#1u\pgfeov}%
    %\pgfkeyssetvalue{\pgfkeyscurrentpath/.@body}{#2}%
  },
  .xparse style/.code 2 args=%
    \pgfkeysalso{\pgfkeyscurrentpath/.xparse code={#1}{\pgfkeysalso{#2}}},
}
\newcommand*\doKey[2]{\texttt{\detokenize{#1}} & \pgfkeys{#1}
  \ifx\\#2\\\else\par\def\tt##1 {\texttt{##1} }\sffamily$\to$ #2\fi\\}
\parindent=0pt
\begin{document}
\pgfkeys{my key1/.xparse code   = { o }{Required1: #2, Optional1: #1}}
\pgfkeys{my key2/.xparse code   = { o }{Required2: #2, Optional2: #1}}
\pgfkeys{my style/.xparse style = { o o }%
                {my key1 = [1]{#1}, /utils/exec=\par, my key2 = {#2}}}

\begin{tabularx}{\linewidth}{l X}
  \doKey{my key1  = [Hello]{John}}{}
  \doKey{my key2  = John2        }{}
  \doKey{my style = [Foo]Bar     }{\tt Bar is lost (and no error message)}
\end{tabularx}

\vspace{1ex}% without the #4
\pgfkeys{omo/.xparse code={omo}{Opt1: #1, Req2: #2, Opt2: #3}}
\begin{tabularx}{\linewidth}{l X}
  \doKey{omo = [opt1]{Foo}[opt3]}{}
  \doKey{omo = [opt1]{FooBar}   }{}
  \doKey{omo = {FooBar}         }{\tt ooBar is lost}
  \doKey{omo = {Foo}Bar         }{\tt Bar is lost}
\end{tabularx}

\vspace{1ex}% as above but with #4 after the #2
\pgfkeys{omo/.xparse code={O{--}mO{--}}{Opt1: #1, Req2: #2#4, Opt2: #3}}
\begin{tabularx}{\linewidth}{l X}
  \doKey{omo = [opt1]{Foo}[opt3]}{}
  \doKey{omo = [opt1]{FooBar}   }{}
  \doKey{omo = {FooBar}         }{ooBar is lost}
  \doKey{omo = {Foo}Bar         }{Bar is caught}
\end{tabularx}
\end{document}
1
  • This was exactly what I needed! Thanks! Commented Nov 7, 2022 at 8:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .