包含 \par 的密钥的密钥过滤器的 PGF 密钥错误

包含 \par 的密钥的密钥过滤器的 PGF 密钥错误

我尝试用它pgfkeys来存储一些文本以供日后使用。最终目标是先使用密钥过滤器处理选项,然后将其应用于文本,然后再进行存储。

在下面的 MWE 中,如果它尝试存储包含 的文本\par,则使用 时会出现错误pgfkeysfiltered,但使用 时不会出现错误pgfkeys,您可以通过取消注释注释行来查看。我知道我在下面的示例中并没有真正使用关键过滤器,但这是相当简单的。

\documentclass{article}

\usepackage{pgfkeys}

\newcommand{\storecontent}{}
\newcommand{\usecontent}{\storecontent}

\pgfkeys{%
    /test/temp family/.is family,
    /test/store/.code={\renewcommand{\storecontent}{#1}},
    /test/store/.belongs to family=/test/temp family,
    /pgf/key filters/active families/.install key filter
}

\begin{document}

\pgfkeys{/test/store={first \par test}}

\usecontent

\pgfkeys{/test/temp family/.activate family}
%\pgfkeysfiltered{/test/store={second \par test}}

\usecontent

\end{document}

有什么办法可以解决这个问题吗?或者这是故意的吗?

答案1

使用的某些宏\pgfkeysfiltered是非的\long,因此它不接受包含\par标记的参数。

在这些情况下,假设 a\par有意义(由您来确保它有意义),您可以使用“几乎”同义词\endgraf。但是,请注意,在某些地方 LaTeX 会重新定义\par但不是\endgraf

另一种策略可能是

\newcommand{\notpar}{\par}

\notpar并在有问题的地方使用。


您可以尝试修补所涉及的命令;在这些咒语之后我没有收到任何错误(在之后发出\usepackage{pgfkeys}):

\makeatletter

\begingroup
\toks0=\expandafter{\pgfkeysfiltered@@install{#1}{#2}}
\edef\x{\endgroup
  \long\def\noexpand\pgfkeysfiltered@@install##1##2{\the\toks0 }}\x

\begingroup
\toks0=\expandafter{\pgfkeys@install@filter@and@invoke{#1}}
\edef\x{\endgroup
  \long\def\noexpand\pgfkeys@install@filter@and@invoke##1{\the\toks0 }}\x

\makeatother

这使得这些宏接受包含的参数\par


正如建议的那样这个答案另外,以下更通用的方法也可以起作用:

\def\longpatch#1{\expandafter\getparts\meaning#1\longpatch
  \begingroup\edef#1{\long\def\noexpand#1\the\toks0 {\the\toks2}}%
  \scantokens\expandafter{\expandafter\endgroup#1}}
\def\getparts#1:#2->#3\longpatch{\toks0={#2}\toks2={#3}}

\makeatletter
\longpatch\pgfkeysfiltered@@install
\longpatch\pgfkeys@install@filter@and@invoke
\makeatother

如何找到要修补的宏?嗯,原始代码中的错误是

! Paragraph ended before \pgfkeysfiltered@@install was complete.

所以你申请

\makeatletter
\longpatch\pgfkeysfiltered@@install
\makeatother

(添加上述通用代码后)。现在你会得到类似的错误

! Paragraph ended before \pgfkeys@install@filter@and@invoke was complete.

所以你知道你必须添加

\longpatch\pgfkeys@install@filter@and@invoke

这些必须位于\makeatother和之间\makeatletter,因为宏的@名称中包含。

答案2

TeX 有一个旧特性,即宏可以定义短的/*正常* 或长的。 仅有的长的宏可能包含段落分隔符,可以是显式分隔符(\par)或隐式分隔符(两个连续的换行符)。如果宏参数未正确关闭,则实现此功能以快速导致错误,因为当时(20 世纪 70 年代等)计算时间非常昂贵。如今它已经失去了其重要性,LaTeX 将\newcommand宏定义为长的默认情况下(带星号的版本没有)。

显然,中的一些内部宏\pgfkeyfiltered未定义为 long,因此如果内容包含,则会引发 TeX 错误\par。这很可能是一个错误,应报告给代码作者。您可以通过隐藏\parTeX 中的来解决此问题,例如使用或包含类似 的\csname par\endcsname宏。\par\newcommand\mypar{\par}

答案3

这是 egreg 解决方案的变体之一。任何调用的命令修补方案\scantokens都假定该命令是可扫描的。这不能想当然。因此,命令修补理想情况下应该包括对命令可扫描性的测试。

\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\def\changecmdprefix{\@testopt\ch@ngecmdprefix{}}
\def\ch@ngecmdprefix[#1]#2{%
  \begingroup
  \edef\x{\def\noexpand\getparts####1\detokenize{macro}:####2->####3}%
  \x\changecmdprefix{%
    \scantokens{\endgroup
      \csname @\ifx\@empty#1\@empty first\else second\fi oftwo\endcsname
      {##1}{#1}\def#2##2{##3}%
    }%
  }%
  \expandafter\getparts\meaning#2\changecmdprefix
}
\changecmdprefix[\long]\pgfkeysfiltered@@install
\changecmdprefix[\protected\long]\pgfkeys@install@filter@and@invoke
\makeatother

\begin{document}
x
\end{document}

使用密钥过滤鍵盤看起来是一件很复杂的事。下面是一种方法斯基瓦尔包裹:

\documentclass{article}
\usepackage{skeyval,xcolor}
\newcommand{\storecontent}{}
\newcommand{\usecontent}{%
  \ifx\storecontent\@empty
    \@@warning{\noexpand\storecontent is empty}%
  \else
    \storecontent
  \fi
}
\def\savpar{\par}
\def\bigvskip{\par\bigskip}
\def\showfam{Family: \skvcurrentfamily\par}

\directkeys{%
  % Define keys in two families:
  .families={family1,family2},
  .define keys={
    .ord/store//\skvappendtomacro\storecontent{#1},
    .ord/remove//\skvpatchcmd\storecontent{#1}{}{}{
      \@@warning{No token '\detokenize{#1}' in command \string\storecontent}
    }
  }
}    
\begin{document}
\directkeys{%
  % Set keys in only family1:
  .family=family1,
  .set keys={
    store={\showfam first test\bigvskip},
    store={\showfam second test\bigvskip}
  },
  .exec code=\usecontent{\color{red}\savpar\hrule\bigvskip},
  % Set keys in only family2:
  .family=family2,
  .set keys={
    remove={\showfam first test\bigvskip},
    remove={\showfam second test\bigvskip},
    store={\showfam third test\bigvskip},
    store={\showfam fourth test\bigvskip}
  },
  .exec code=\usecontent
}
\end{document} 

在此处输入图片描述

其他过滤处理程序包括

.prefix, .change prefix, .family, .change family, .families, .change families, 
.add family, .add families, .ignore family, .ignore families, .restore family,
.restore families, .paths, .change path, .change paths, .add paths, .ignore paths,
.restore paths, .ignore keys, .restore keys

相关内容