A definição personalizada de \ref causa problemas dentro do argumento de \caption

A definição personalizada de \ref causa problemas dentro do argumento de \caption

Estou tentando implementar uma versão personalizada do \refcomando. Descobri que, para poder redefinir esse comando, preciso fazê-lo, \AtBeginDocumentcaso contrário, minha alteração parecia ter sido substituída por outra coisa.

Agora funciona como esperado, exceto ao usar \refdentro de a, \captiono que produzirá o seguinte erro:

Argument of \@caption has an extra }. \caption{Test here \ref{sec:Section}}
Paragraph ended before \@caption was complete. \caption{Test here \ref{sec:Section}}

Aqui está um MWE reproduzindo o problema:

\documentclass{article} 
\usepackage{xstring}

\AtBeginDocument{%
    \let\refCopy\ref
    \renewcommand{\ref}[1]{%
        \IfBeginWith{#1}{eq:}{
            (\refCopy{#1})
        }{
        \refCopy{#1}
    }
}
}

\setcounter{errorcontextlines}{999}

\begin{document}
    \section{Section}
    \label{sec:Section}

    \begin{figure}
        \caption{Test here \ref{sec:Section}}
    \end{figure}
\end{document}

Se eu remover minha implementação personalizada do \refdocumento, ele será compilado perfeitamente.

O que está causando esse problema e como posso evitá-lo?

Responder1

Sua (re)definição \reftorna-o um comando chamado "frágil". Isso importa se \reffor usado no argumento de um "comando de movimentação" - como \caption. Você precisa mudar

\caption{Test here \ref{sec:Section}}

para

\caption{Test here \protect\ref{sec:Section}}

Além disso, você precisa ter certeza de que nenhum espaço em branco falso seja introduzido antes ou depois da chamada de referência cruzada. Eu sugiro que você mude

    \renewcommand{\ref}[1]{%
        \IfBeginWith{#1}{eq:}{
            (\refCopy{#1})
        }{
        \refCopy{#1}
    }
}

para

    \renewcommand{\ref}[1]{%
        \IfBeginWith{#1}{eq:}{%
            (\refCopy{#1})%
        }{%
        \refCopy{#1}%
    }%
}%

Você consegue identificar as seis novas instâncias de %?


Termo aditivo: Como @AlanMunn apontou em um comentário, as \protectdiretivas não são necessárias se alguém carregar o etoolboxpacote e substituí-lo \renewcommand{\ref}...por \renewrobustcmd{\ref}....

Formatando o código como

\renewcommand{\ref}[1]{%
  \IfBeginWith{#1}{eq:}{%
    (\refCopy{#1})%
  }{%
    \refCopy{#1}%
  }%
}%

pode torná-lo mais legível, pois os níveis são mais evidentes.

informação relacionada