Documentação tcolorbox: documentando caminhos-chave dinâmicos

Sep 27 2020

Estou documentando um pacote que possui certos caminhos-chave que são criados dinamicamente (por algum comando ou em tempo real por meio do .unknownmanipulador pgfkeys ).

Quero documentar essas chaves com o docKeyambiente de tcolorbox, mas os primeiros argumentos (tanto os opcionais quanto os obrigatórios) não funcionam com macros como \meta{}.

Existe uma maneira adequada de fazer isso?

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}

Respostas

5 muzimuzhiZ Sep 27 2020 at 12:53

tcolorboxassume que o valor do primeiro argumento opcional do docKeyambiente (também o valor da chave /tcb/doc keypath) é totalmente expansível e o usa na impressão, nome do rótulo e nome do índice. Como \meta{...}não é totalmente expansível, são gerados erros.

No exemplo a seguir, divido a função de doc keypathem quatro:

  • nome da chave impressa
  • nome do rótulo, pode ser definido por doc keypath label
  • nome do índice impresso, pode ser definido por doc keypath index
  • nome do índice classificado, pode ser definido por doc keypath sort index.

Modificado doc keypathirá definir todos os quatro, e uma nova chave doc formatted keypathdefine o primeiro e o terceiro.

Uso:

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

See \refKey{/keypath/my key}.

Exemplo completo:

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

4 meide Sep 27 2020 at 19:30

Obrigado pelas respostas detalhadas, @Marijn & @mushimushiZ! Estou estudando-os por ideias e técnicas.

Quanto a este problema específico, verifica-se que é possível fazer com as opções fornecidas pelo próprio tcolorbox, por meio da doc labelchave, se as macros forem mantidas na própria chave (e não no argumento caminho opcional):

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

3 Marijn Sep 27 2020 at 11:54

O problema é que tanto o argumento opcional quanto o obrigatório são usados ​​para criar um rótulo para referência \refKey, ou seja \refKey{/mypackage/keypath/my key},. No valor de um rótulo, comandos como \metanão são permitidos.

Você pode modificar a definição de docKeyligeiramente para contornar o problema, definindo o rótulo para ser apenas o nome sem o prefixo opcional. Uma maneira de fazer isso é copiar a definição da tcolorboxfonte e usar \RenewDocumentEnvironmentpara fazer as alterações.

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}

Resultado:

Observação: talvez \textit{<keypath>}seja melhor do que \meta{keypath}: