4

I'm documenting a package that has certain key paths which are created dynamically (either by some command or on the fly via the pgfkeys .unknown handler).

I want to document these keys with the docKey environment from tcolorbox, but the first arguments (both the optional and the mandatory) do not work with macros such as \meta{}.

Is there a proper way of doing this?

MWE:

\documentclass{article}

\usepackage{tcolorbox}
\tcbuselibrary{documentation}

\begin{document}

\begin{docKey}
    [mypackage/keypath]
    {my key}
    {=value}
    {}
    The \texttt{keypath} part of the key path is dynamic. Using \meta{keypath} breaks.

    Putting both in the mandatory argument as \verb|{\meta{keypath}/my key}| also breaks.
    
    What is the best alternative?
\end{docKey}

\end{document}

3 Answers 3

7

tcolorbox assumes the value of first optional argument of docKey environment (also the value of key /tcb/doc keypath) is fully expandable and uses it in printing, label name, and index name. Since \meta{...} is not fully expandable, errors are raised.

In the following example, I split the role of doc keypath into four:

  • printed key name
  • label name, can be set by doc keypath label
  • printed index name, can be set by doc keypath index
  • sorted index name, can be set by doc keypath sort index.

Modified doc keypath will set all four of them, and a new key doc formatted keypath sets both the first and the third.

Usage:

\begin{docKey}
    [keypath][doc formatted keypath=\meta{keypath}]
    {my key}
    {=value}
    {}
\end{docKey}

See \refKey{/keypath/my key}.

Full example:

\documentclass{article}
\usepackage{tcolorbox}
\tcbuselibrary{documentation}
\makeindex

\makeatletter
\newif\iftcb@doc@formatted@keypath

\tcbset{
  doc keypath/.style={%
    doc@keypath={#1},
    doc keypath label={#1},
    doc keypath index={#1},doc keypath sort index={#1},
  },
  doc@keypath/.store in=\kvtcb@doc@keypath,
  doc formatted keypath/.code=%
    \tcb@doc@formatted@keypathtrue
    \pgfkeysalso{doc@keypath=#1,doc keypath index=#1},
%  doc keypath name/.store in=\kvtcb@doc@keypath,
  doc keypath label/.store in=\kvtcb@doc@keypath@label,
  doc keypath index/.store in=\kvtcb@doc@keypath@index,
  doc keypath sort index/.store in=\kvtcb@doc@keypath@sortindex,
}

\ExplSyntaxOn
\cs_set_nopar:Npn \__tcobox_print_key:
  {
    \textcolor{\kvtcb@col@key}{\tcb@doc@bfseries\ttfamily
    \tl_if_empty:NF \kvtcb@doc@keypath
      {
        \iftcb@doc@formatted@keypath
          /\kvtcb@doc@keypath/
        \else
          /\__tcobox_sanitize_tl:V\kvtcb@doc@keypath/
        \fi
      }
    \__tcobox_sanitize_tl:V\kvtcb@doc@name}
  }

\cs_set_nopar:Npn \__tcobox_index_key:
  {
    \iftcb@doc@toindex
      \kvtcb@index@command{\kvtcb@doc@sortindex\idx@actual\tcbIndexPrintKeyCA{\kvtcb@doc@index}}
      \tl_if_empty:NTF \kvtcb@doc@keypath@index
        {
          \kvtcb@index@command{\kvtcb@text@keys\idx@level\kvtcb@doc@sortindex\idx@actual\tcbIndexPrintKeyC{\kvtcb@doc@index}}
        }
        {
          \kvtcb@index@command{\kvtcb@text@keys\idx@level\kvtcb@doc@keypath@sortindex\idx@actual\tcbIndexPrintKeyC{/\kvtcb@doc@keypath@index/}
            \idx@level\kvtcb@doc@sortindex\idx@actual\tcbIndexPrintKeyC{\kvtcb@doc@index}}
        }
    \fi
  }

\cs_set:Npn \__tcobox_doc_head_key:
  {
    \__tcobox_print_key:
    \__tcobox_index_key:
    \tl_if_empty:NTF \kvtcb@doc@keypath@label
      {
        \seq_if_in:NVF \g__tcobox_label_seq \kvtcb@doc@label
          {
            \protected@edef\@currentlabel{\kvtcb@doc@name}
            \label{key:\kvtcb@doc@label}
            \seq_gput_left:NV \g__tcobox_label_seq \kvtcb@doc@label
          }
      }
      {
        \tl_set:Nx \l_tmpa_tl {/\kvtcb@doc@keypath@label/\kvtcb@doc@label}
        \seq_if_in:NVF \g__tcobox_label_seq \l_tmpa_tl
          {
            \protected@edef\@currentlabel{/\kvtcb@doc@keypath/\kvtcb@doc@name}
            \label{key:/\kvtcb@doc@keypath@label/\kvtcb@doc@label}
            \seq_gput_left:NV \g__tcobox_label_seq \l_tmpa_tl
          }
      }
    {\ttfamily\kvtcb@doc@parameter}
    \tcb@doc@do@description
  }
\ExplSyntaxOff
\makeatother


\begin{document}

\begin{docKey}
    [keypath][doc formatted keypath=\meta{keypath}]
    {my key}
    {=value}
    {}
    The \texttt{keypath} part of the key path is dynamic. Using \meta{keypath} breaks.

    Putting both in the mandatory argument as \verb|{\meta{keypath}/my key}| also breaks.
    
    What is the best alternative?
\end{docKey}

See \refKey{/keypath/my key}.

\printindex

\end{document}

enter image description here enter image description here

4

Thanks for the detailed answers, @Marijn & @mushimushiZ! I am studying them for ideas and techniques.

As for this specific problem, it turns out that it is possible to do with the options provided by tcolorbox itself, via the doc label key, if the macros are kept to the key itself (and not in the optional path argument):

\documentclass{article}

\usepackage{tcolorbox}
\tcbuselibrary{documentation}

\makeindex

\begin{document}

\begin{docKey}
    [mypackage]
    [doc label=keypath/mykey]
    {\meta{keypath}/my key}
    {=value}
    {}
    The \texttt{keypath} part of the key path is dynamic and corresponds to ...
\end{docKey}

Can be referenced: \refKey{/mypackage/keypath/mykey}

\printindex

\end{document}

document

index

3

The issue is that both the optional and the mandatory argument are used to create a label for referencing with \refKey, i.e., \refKey{/mypackage/keypath/my key}. In the value of a label commands such as \meta are not allowed.

You can modify the definition of docKey slightly to circumvent the issue, by defining the label to be just the name without the optional prefix. One way of doing that is to copy the definition from the tcolorbox source and use \RenewDocumentEnvironment to make the changes.

MWE:

\documentclass{article}

\usepackage{tcolorbox}
\tcbuselibrary{documentation}
\makeatletter
\RenewDocumentEnvironment{docKey}{ O{} +O{} m m +m }{\tcbset{#2,doc description={#5}}%
  \begin{tcb@manual@entry}%
  \begin{tcb@doc@head}{doc@head@key}%
  \ifblank{#1}{%
    \tcb@Print@Key{#3}\tcb@index@Key{#3}\protected@edef\@currentlabel{#3}\label{key:#3}{\ttfamily #4}%
  }{%
    \tcb@Print@Key{/#1/#3}%
    % CHANGE OF LABEL IN FOLLOWING LINE %
    \tcb@index@KeyPath{#1}{#3}\protected@edef\@currentlabel{/#1/#3}\label{key:#3}{\ttfamily #4}%
  }%
  \tcb@doc@do@description%
  \end{tcb@doc@head}\nobreak\tcbset{before upper=}\kvtcb@doc@body@key@before\ignorespaces}%
  {\ifvmode\else\unskip\fi\kvtcb@doc@body@key@after\end{tcb@manual@entry}}
\makeatother


\begin{document}
\begin{docKey}
    [mypackage/\meta{keypath}]
    {my key}
    {=value}
    {}
    The \texttt{keypath} part of the key path is dynamic. Using \meta{keypath} breaks.

    Putting both in the mandatory argument as \verb|{\meta{keypath}/my key}| also breaks.
    
    What is the best alternative?
\end{docKey}

See \refKey{my key}.

\end{document}

Result:

enter image description here

Remark: maybe \textit{<keypath>} is nicer than \meta{keypath}:

enter image description here

2
  • 1
    Running your example, I get error undefined control sequence \tcb@Print@Key with tcolorbox v4.40. Sep 27, 2020 at 12:43
  • @muzimuzhiZ that could indeed be a version problem, I have v4.12. However, probably a very similar approach could be implemented for a newer version, by copying the actual \NewDocumentEnvironment definition from the source of the newer version and changing the label argument (I did not test that theory though, maybe other issues will appear when that is tried).
    – Marijn
    Sep 27, 2020 at 13:34

You must log in to answer this question.

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