![脆弱命令的输出可以是超链接地址吗?](https://linux22.com/image/242930/%E8%84%86%E5%BC%B1%E5%91%BD%E4%BB%A4%E7%9A%84%E8%BE%93%E5%87%BA%E5%8F%AF%E4%BB%A5%E6%98%AF%E8%B6%85%E9%93%BE%E6%8E%A5%E5%9C%B0%E5%9D%80%E5%90%97%EF%BC%9F.png)
这个问题导致了一个新的方案的出现:
bibleref-mouth
我正在尝试编写一个带有可选参数的命令,并将输出提供给该\url
命令。我的动机是使用bibleref
包。该包已经定义了一个带有可选参数的 API,所以我不想更改语法。
\url{\bibleverseurl{Amos}(3:7)}
但带有可选参数的命令显然太脆弱,无法被使用\url
。故事在我的示例代码中。
\documentclass{article}
\usepackage{makerobust}
\usepackage{hyperref}[2011/04/09 v6.82f]
% Macro that takes an argument and encloses it in angle brackets
\protect\def\parenmatch(#1){%
MATCH$\langle#1\rangle$%
}
% Macro that does not take an argument
\protect\def\parenmismatch{%
MISMATCH%
}
% Check whether there is a parenthesis in the input, then call
% \parenmatch or \parenmismatch
\makeatletter
\newcommand*{\matcher}{%
\@ifnextchar{(}%
\parenmatch%
\parenmismatch%
}
\makeatother
% The \matcher command is probably fragile, so try to make it robust.
% Actually, I found that \matcher behaves identically to \robustmatcher
% in the examples below.
\DeclareRobustCommand{\robustmatcher}{\matcher}
\begin{document}
% This line correctly produces: MATCH<arg>
\robustmatcher(arg)
% This line correctly produces: MISMATCH!
\robustmatcher!
% This block emits an error: ! Use of \parenmatch doesn't match its definition.
% If I loosen the restrictions on the \parenmatch arguments, it
% only produces a different warning: ! Missing control sequence inserted.
% The evaluation does not respect protected commands, so it either fails to
% compile or it creates incorrect output.
% A better option is below.
\edef\problem{\robustmatcher()}
\problem
% This block correctly produces: MATCH<arg>
% As long as I use \protected@edef instead of \edef, it compiles successfully.
\makeatletter
\protected@edef\captured{\robustmatcher(arg)}
\makeatother
\captured
% Now I would like to use the command output as a link target.
% This was my goal all along.
% But the link doesn't have the address I would expect. It shows in the document as:
% \protect\let\reserved@d=(\def\defMISMATCH{MISMATCH}\futurelet\@let@token\letMATCH$\delimiter"426830Aarg\delimiter"526930B$
% I tried sprinkling \protect and \expandafter commands in the definitions and
% applications, but nothing has helped.
\url{\captured}
\end{document}
有什么事情hyperref
会使这一切变得不可能吗?我希望能够以某种方式重新排序求值,以便可以\url
安全地传递已扩展的纯字符串。
答案1
您面临的问题是无法以可扩展的方式解析可选参数。因此,\url
永远不会看到宏的扩展。在最好的情况下(即,如果宏变得健壮),宏根本不会扩展,在最坏的情况下,它们会部分扩展,并且通常会出现一些! Undefined control sequence
错误。
因此,最好的办法是将\url
命令放在最终宏内,而不是放在宏周围。这样,带有可选参数的命令就“超出”了所有其他命令,并且可以按您的意愿不扩展。
\documentclass{article}
\usepackage{hyperref}[2011/04/09 v6.82f]
\def\urlmatcher{\@ifnextchar{(}\urlparenmatch\urlparenmismatch}
\def\urlparenmatch(#1){\url{MATCH(#1)}}
\def\urlparenmismatch{\url{MISMATCH!}}
\begin{document}
\urlmatcher Hello, world!
\urlmatcher(arg)
\end{document}
为了帮助您调试,您可以使用\show
,非常有用的 TeX 原语(用作例如\show\urlmatcher
,或在您的示例中,\show\captured
)。我有时也使用\tracingall
。