6

I'm trying to use pgfkeys to collect information that will be passed to a clist. But, I'm not able to get this to work correctly. It seems that I'm getting brackets around the list and is prevent \clist_map_inline:Nn from parsing its argument as desired.

Here's a MWE:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\clist_new:N \l_ae_itemize_element_clist
\cs_new:Npn \_ae_itemize:n #1 
  {
    \clist_set:Nn \l_ae_itemize_element_clist {#1}
    \begin{itemize}
      \clist_map_inline:Nn \l_ae_itemize_element_clist { \item ##1 }
    \end{itemize}
  }
\NewDocumentCommand{\aeItemizeElements}{m}
  { \_ae_itemize:n {#1} }
\ExplSyntaxOff

\usepackage{pgfkeys}
\pgfkeys
  {
    /ae/list example/.cd,
    title/.initial =,
    content/.initial = {},
  }

\def\aeSet#1{\pgfkeys{/ae/list example/.cd,#1}}
\def\aeGet#1{\pgfkeysvalueof{/ae/list example/#1}}

\NewDocumentCommand{\aeItemizeA}{ m }
  {\aeSet{#1}%%'
   \noindent\textbf{\aeGet{title}}%%'
   \aeItemizeElements{\aeGet{content}}
  }

\pagestyle{empty}
\begin{document}

\aeItemizeA{title=Works as expected,content=\textbf{only one item}}

\aeItemizeA{title=Not iterating over content,content={first item, second item}}

\end{document}

To get the second list to be formatted correctly, I could do something like

\clist_set:Nx \l_ae_itemize_element_clist {#1}

But that crashes on the first list because it's expanding the content.

Is there a way to automatically strip away outer brackets, if they're there, while not expanding the content, or doing nothing if there are no outer brackets?

6
  • Is there a specific reason you've chosen pgfkeys over l3keys?
    – cgnieder
    Aug 17, 2013 at 17:10
  • @cgnieder Mostly because I'm trying to learn how to use pgfkeys. But also, pgfkeys seems to have a simpler syntax for some things such as for saving and storing values.
    – A.Ellett
    Aug 17, 2013 at 17:13
  • I don’t know much about expl3 (and without taking a closer look at your example) but removing {} should be easy doable with a combination of \@ifnextchar\bgroup and \@firstofone. Aug 17, 2013 at 17:36
  • It isn't really a matter of having {} around the list, it just isn't a list at all until you execute the pgf keys instructions, the macro is: > \l_ae_itemize_element_clist=macro: ->\aeGet {content}. Aug 17, 2013 at 18:05
  • @Qrrbrbirlbel If you could write that up as an answer, I would accept. Changing content/.initial to content/.code = {\@ifnextchar\bgroup{\expandafter\@ae@remove@bracket\@firstofone}{\@ae@remove@bracket{#1}}} and defining \def\@ae@remove@bracket#1{\def\mycontent{#1}} did the trick.
    – A.Ellett
    Aug 17, 2013 at 18:07

3 Answers 3

9

enter image description here

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\clist_new:N \l_ae_itemize_element_clist
\cs_new:Npn \_ae_itemize:n #1 
  {
    \clist_set:No \l_ae_itemize_element_clist {\romannumeral`\^^@#1}
    \begin{itemize}
      \clist_map_inline:Nn \l_ae_itemize_element_clist { \item ##1 }
    \end{itemize}
  }
\NewDocumentCommand{\aeItemizeElements}{m}
  { \_ae_itemize:n {#1} }
\ExplSyntaxOff

\usepackage{pgfkeys}
\pgfkeys
  {
    /ae/list example/.cd,
    title/.initial =,
    content/.initial = {},
  }

\def\aeSet#1{\pgfkeys{/ae/list example/.cd,#1}}
\def\aeGet#1{\pgfkeysvalueof{/ae/list example/#1}}

\NewDocumentCommand{\aeItemizeA}{ m }
  {\aeSet{#1}%%'
   \noindent\textbf{\aeGet{title}}%%'
   \aeItemizeElements{\aeGet{content}}
  }

\pagestyle{empty}
\begin{document}

\aeItemizeA{title=Works as expected,content=\textbf{only one item}}

\aeItemizeA{title=Not iterating over content,content={first item, second item}}

\end{document}
8
  • 2
    What's the \clist_set:No \l_ae_itemize_element_clist {\romannumeral`\^^@#1} business about? Looks like an f-type expansion to me
    – Joseph Wright
    Aug 17, 2013 at 18:25
  • I know \romannumeral, but how is that that helps here? I'm intrigued and quite mystified by this magic.
    – A.Ellett
    Aug 17, 2013 at 18:26
  • @JosephWright the whole point of l3 expansion control is not to have to do that, but habits die hard:-) Aug 17, 2013 at 18:30
  • @JosephWright Thank you for that comment. By defining a variant, \clist_set:Nf, I was able to get the effect desired without having to detect braces.
    – A.Ellett
    Aug 17, 2013 at 18:30
  • 1
    @A.Ellett See texdev.net/2011/07/05/expansion-using-romannumeral for how the magic works
    – Joseph Wright
    Aug 17, 2013 at 18:31
5

Here is a pgfkeys-only approach (and a little bit pgffor for the .list handler).

The \romannumeral trick is used to get the content of the content key without expanding it (we can’t edef it because of possible unexpandable stuff (like \textbf)). This is only used to test the content key for contents. It is not needed otherwise.

A \rumannumeral-less version is given below with two expansions (including nine \expandafters).

The \aeGet{content} needs to be expanded four times so that we get to its content.

  1. \pgfkeysvalueof{/ae/list example/content}
  2. \csname pgfk@/ae/list example/content\endcsname
  3. \pgfk@/ae/list example/content
  4. <content>

Of course you can shorten this by giving the raw \csname version (similar to the \romannumeral-less version of \def\aeTemp).

Code

\documentclass{article}
\usepackage{pgfkeys,pgffor}
\def\aeSet{\pgfqkeys{/ae/list example}}
\def\aeGet#1{\pgfkeysvalueof{/ae/list example/#1}}
\aeSet{
  title/.initial=,
  content/.initial=,
  itemize/.code={\item #1}%
}
\newcommand{\aeItemizeA}[1]{%
  \begingroup
    \aeSet{#1}
    \noindent\textbf{\aeGet{title}}%
    \expandafter\def\expandafter\aeTemp\expandafter{\romannumeral-`0\aeGet{content}}%
    % or:
%     \expandafter\expandafter\expandafter\def
%     \expandafter\expandafter\expandafter\aeTemp
%     \expandafter\expandafter\expandafter{\csname pgfk@/ae/list example/content\endcsname}%
    \itemize
      \ifx\aeTemp\empty
        \item \itshape empty!
      \else
        \aeSet{itemize/.list/.expand twice/.expand twice=\aeGet{content}}
      \fi
    \enditemize
  \endgroup
}

\pagestyle{empty}
\begin{document}
\aeItemizeA{title=No entry given!}

\aeItemizeA{title=Works as expected,content=\textbf{only one item}}

\aeItemizeA{title=Not iterating over content,content={first item, second item}}
\end{document}

Output

enter image description here

5

It's easier without pgfkeys, in my opinion.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\clist_new:N \l_ae_itemize_element_clist
\cs_generate_variant:Nn \clist_map_inline:nn { f }
\cs_new:Npn \_ae_itemize:n #1 
 {
  \begin{itemize}
    \clist_map_inline:fn {#1} { \item ##1 }
  \end{itemize}
 }
\NewDocumentCommand{\aeItemizeElements}{m}
 { \_ae_itemize:n {#1} }

\keys_define:nn { ae/list }
 {
  title   .tl_set:N  = \l_ae_list_title_tl,
  title   .initial:n = {},
  content .tl_set:N  = \l_ae_list_content_tl,
  content .initial:n = {},
 }

\NewDocumentCommand{\aeSet}{m}
 {
  \keys_set:nn { ae/list } { #1 }
 }

\NewDocumentCommand{\aeItemizeA}{ m }
 {
  \group_begin:
  \aeSet{#1}
  \noindent\textbf{\l_ae_list_title_tl}
  \aeItemizeElements{\l_ae_list_content_tl}
  \group_end:
 }
\ExplSyntaxOff

\pagestyle{empty}
\begin{document}

\aeItemizeA{title=Works as expected,content=\textbf{only one item}}

\aeItemizeA{title=Iterating over content,content={first item, second item}}

\newcommand{\test}{Item A, Item B}

\aeItemizeA{title=This has a control sequence for content, content=\test}

\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 .