7

I try to make just a very minimalist example of pgfkeys — or other keystyle parameters package —, with a command like this one:

\mycommand{title="The new book", author="John Doe"}

to get a behavior like this:

Writer John Doe

Title The new book

And, possibly, if on key is missing, it line will not appear. For example, the command

\mycommand{title="The new book"}

Will only print:

Title The new book

I check in the web and also StackExchange but I only find very complicated examples including so much other things not directly related to key-value syntax.

So, how can I just put such a minimal key-value command?

3
  • well this is not really a "minimal" command. Beside setting up the keyval syntax you also need code to test for empty values, and to store the fix words (Writer, Title). Beside this your question is missing where you want to use this command, if the formatting should use a description list or something else and if there will be more keys ... May 12, 2020 at 12:57
  • What is the expected behavior if one of the keys is empty (ex: \mycommand{title=The new book,author=}? What if both are missing or empty (ex: \mycommand{})? May 12, 2020 at 13:40
  • @UlrikeFischer I just try to understand the mechanism of the key-value commands. So yes it’s not too complex example, but a so minimalist example will not be enough instructive.
    – fauve
    May 12, 2020 at 16:24

5 Answers 5

5

I'm not sure what you really need. This can be something to start...

Assigning to title or author actually runs the code defined in pgfkeys. Nothing is stored.

\documentclass{article}

\usepackage{pgfkeys}

\pgfkeys{
    /bibliography/.cd, % family
    title/.code = {\ifx#1\empty A\else{\textbf{Title} #1\par}\fi},
    author/.code = {\ifx#1\empty A\else{\textbf{Author} #1\par}\fi},
}

\newcommand{\mycommand}[1]{
    \pgfkeys{
        /bibliography/.cd,
        #1,
    }
    \pgfkeysvalueof{/bibliography/title}
    \pgfkeysvalueof{/bibliography/author}
}
\begin{document}
One:\par
\mycommand{title = {Tikz \& PGF}, author={JD}}

Two:\par
\mycommand{author={JD}}

Three:\par
\mycommand{title = {Tikz \& PGF}}

Four:\par
\mycommand{}

Five:\par
\mycommand{title=, author=}

\end{document}

Edit

Please note that the .code is executed at "parameter time", so \mycommand{author={JD}, title = {Tikz \& PGF}} will print the author before the title.

Edit 2

This version uses \bibtitle and \bibauthor to store the data. As theses macros are global, you have to clear them (set to empty) in each call, otherwise previous values are remembered.

\documentclass{article}

\usepackage{pgfkeys}

\pgfkeys{
    /bibliography/.cd, % family
    title/.store in = \bibtitle,
    author/.store in = \bibauthor,
}

\newcommand{\mycommand}[1]{
    \pgfkeys{
        /bibliography/.cd,
        title = {},  % clear title
        author = {}, % clear author
        #1,
    }
    \ifx\bibtitle\empty\else\textbf{Title:} \bibtitle\par\fi
    \ifx\bibauthor\empty\else\textbf{Author:} \bibauthor\par\fi
}
\begin{document}
One:\par
\mycommand{title = {Tikz \& PGF}, author={JD}}
\mycommand{author={JD}, title = {Tikz \& PGF}}
    
\vspace{2ex}
Two:\par
\mycommand{author={JD}}

\vspace{2ex}
Three:\par
\mycommand{title = {Tikz \& PGF}}

\vspace{2ex}
Four:\par
\mycommand{}

\vspace{2ex}
Five:\par
\mycommand{title=, author=}

\end{document}
4
  • If I understand /bibliography/ is a variable name you choose. It could be anything else? Is it a kind of namespace?
    – fauve
    May 12, 2020 at 16:26
  • 1
    @fauve bibliography is called a family and you can name it as you like. Think about it as a directory tree (as the manual says). Therefore you can have a title inside bibliography which is distinct from another title in other branch of the tree.
    – Jander
    May 12, 2020 at 17:16
  • Additional question: is it possible to force item apparition order? I mean, if I do \mycommand{title="The new book", author="John Doe"} or \mycommand{author="John Doe", title="The new book"} it always give “Writer John Doe Title The new book”.
    – fauve
    May 17, 2020 at 11:16
  • 1
    @fauve Please see the 'EDIT 2' in my answer. Just realized I inverted the lines, so in my example the title comes first...
    – Jander
    Jun 2, 2020 at 17:51
4

Here's an implementation with expl3 keys.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

% define the keys
\keys_define:nn { fauve/books }
 {
  title  .tl_set:N = \l__fauve_books_title_tl, % the title
  author .tl_set:N = \l__fauve_books_author_tl, % the author
 }

% formatting
\NewDocumentCommand{\mycommand}{m}
 {
  \group_begin:
  \keys_set:nn { fauve/books } { #1 } % populate the keys

  % format the book
  \par\addvspace{\topsep}
  \fauve_books_author:V \l__fauve_books_author_tl
  \fauve_books_title:V \l__fauve_books_title_tl
  \par\addvspace{\topsep}
  \group_end:
 }

\cs_new_protected:Nn \fauve_books_title:n
 {
  \tl_if_empty:nF { #1 }
   {
    \par\noindent\textbf{Title:}~#1
   }
 }
\cs_new_protected:Nn \fauve_books_author:n
 {
  \tl_if_empty:nF { #1 }
   {
    \par\noindent\textbf{Writer:}~#1
   }
 }
\cs_generate_variant:Nn \fauve_books_title:n  {V}
\cs_generate_variant:Nn \fauve_books_author:n {V}

\ExplSyntaxOff

\begin{document}

\mycommand{title=The new book, author=John Doe}

\mycommand{title=Another book}

\end{document}

I have separated the formatting commands specific for each key, but that's optional.

enter image description here

2

FWIW, I'll show how to write such a macro in ConTeXt. ConTeXt has a few key-value driven interfaces, but for something as simple as this, I would just piggyback oon the \setupvariables[...][..=..] and \getvariable{...}{...}.

\unprotect
\define\mycommand
    {\dosingleargument\mycommand_setup}

\def\mycommand_setup[#1]%
    {\bgroup
     \setvariables[mycommand][#1]%
     \mycommand_process
     \egroup}

\def\mycommand_process
    {\startlines
     \doifsomething{\getvariable{mycommand}{author}}
        {\bold{Writer:} \getvariable{mycommand}{author}\endgraf}%
     \doifsomething{\getvariable{mycommand}{title}}
        {\bold{Title:} \getvariable{mycommand}{title}}%
     \stoplines}
\protect

\starttext
\mycommand[title={The new book}, author={John Doe}]

\mycommand[title={The new book}]
\stoptext

Note that I modified the interface (use \mycommand[...] instead of \mycommand{...}) to match the ConTeXt convention that arguments in square brackets are for options and arguments in curly brackets are for text which is typeset.

1

With expkv-def you can use data and dataT keys.

A data key will store the information you provide in the value in a macro, but this macro also takes two arguments. If the key wasn't used it'd execute its second argument, while if the key was used it'd execute its first argument and append the value given to it in braces (so as an argument to that code).

A dataT key behaves similar, but the macro will only take one argument and would do nothing if the key wasn't used yet, whereas it would execute its argument and append the given value if it was used.

Just storing the value in a variable would be possible with the store key type instead of data.

\documentclass[]{article}

\usepackage{expkv-def}

\makeatletter
\ekvdefinekeys{fauve}
  {
     data  title  = \fauve@title
    ,dataT author = \fauve@author
    ,dataT year   = \fauve@year
  }
\ekvsetdef\fauve@set{fauve}
\newcommand\mycommand[1]
  {%
    \begingroup
      \fauve@set{#1}%
      \fauve@author{\par\noindent\textbf{Writer} }%
      \fauve@title
        {\par\noindent\textbf{Title} }%
        {\par\noindent\emph{No title}}%
      \fauve@year{\par\noindent\textbf{Year} }%
    \endgroup
  }
\makeatother

\begin{document}
\mycommand{title=The new book,author=John Doe}
\bigskip
\mycommand{title=The new book}
\bigskip
\mycommand{title=The new book,year=2020}
\bigskip
\mycommand{author=John Doe,year=2020}
\end{document}

enter image description here

1

With expkv-cs minimal key=value macros are very easy to implement.

In this case we may also want different behaviour if a key was given or not. This can be achieved relatively easy by one level of indirection, where the first level contains the default value if nothing was supplied, and the second level adds markup/formatting instructions if a key was used. This way we don't need any tests in the macro replacement but can directly use the values (even if they are empty). For this purpose the following uses -internal for the real keys, but adds wrappers around those which add stuff like \par\noindent or similar.

\documentclass[]{article}

\usepackage{expkv-cs}
% declaring the internal keys and the order of output.
\ekvcSplit\mycommand
  {
    % long since it'll get a `\par` token by the frontend key
     long author-internal = {}              % #1
    ,     title-internal  = \emph{No title} % #2
    ,long year-internal   = {}              % #3
  }
  {#1\par\noindent#2#3}

% Declaring the front level keys and how they interact with the internal keys.
% A meta key will set other keys, so if `year=YYYY` is called it'll be
% equivalent to `year-internal=\par\noindent\textbf{Year} YYYY`
\ekvcSecondaryKeys\mycommand
  {
    % for a single key=value pair set by a `meta` secondary key we don't need
    % additional braces guarding the second = in expkv. The following is
    % equivalent to
    % meta author = {author-internal = {\par\noindent\textbf{Writer} #1}}
     meta author = author-internal = {\par\noindent\textbf{Writer} #1}
    ,meta title  = title-internal  = {\textbf{Title} #1}
    ,meta year   = year-internal   = {\par\noindent\textbf{Year} #1}
  }

\begin{document}
\mycommand{title=The new book,author=John Doe}
\bigskip
\mycommand{title=The new book}
\bigskip
\mycommand{title=The new book,year=2020}
\bigskip
\mycommand{author=John Doe,year=2020}
\end{document}

enter image description here

You must log in to answer this question.

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