为什么 \lstset{escapeinside={((}{))}} 不能按预期工作?

为什么 \lstset{escapeinside={((}{))}} 不能按预期工作?

这是我的代码:

\documentclass{article}
\usepackage{listings}
\lstset{basicstyle=\ttfamily,columns=fullflexible}
\begin{document}

\lstset{escapeinside={((:}{:))}}
\begin{lstlisting}
foo
((:\textit{bar}:))
baz
\end{lstlisting}

\lstset{escapeinside={((}{))}}
\begin{lstlisting}
foo
((\textit{bar}))
baz
\end{lstlisting}

\end{document}

输出如下:

在此处输入图片描述

答案1

该包当然允许在 中使用多个开始和结束字符escapeinside,因为代码会根据字符的数量(一个、两个还是两个以上)而变化。在有多个<character>分隔符的情况下,的工作方式是使和escapeinside中的第一个标记成为活动字符,这些活动字符会先查看并检查它们后面是否跟着定义的字符。openclose

在第一个示例中(为了简单起见,我们假设您使用了escapeinside={(:}{:)}),(被设为活动字符,它会向前查看并检查其后面是否跟着一个:字符,如果是,则开始转义。在转义代码中,:被设为活动字符并查找一个)字符。

在第二个示例中,(被设为活动字符,它会向前查找另一个(,并启动转义代码。在转义代码中,)被设为活动字符,并向前查找)。但是它从未找到) 特点,但) 活跃角色,因此它不会结束转义代码,而只是打印 a )。下一个)执行相同操作并打印另一个)1

这个问题在您的第一个例子中没有显示出来,因为激活的字符和寻找的字符不同,所以它没有显示问题。

这里有一个建议,可以修补该问题并使其escapeinside适用于任何字符。但要小心!我没有广泛测试此代码(阅读:除了给定的代码,我根本没有测试)所以我不知道修复此问题是否会破坏其他内容。我会说不会,因为它是一个非常具体的代码,似乎没有在其他地方使用,但警告在这里,所以你不能事后责怪我 :-)

我修改了两个定义,\lst@CDefIt\lst@IfNextChars@@,分别用于两个标记分隔符和三个或更多标记分隔符。我将标记比较从更改为\ifx\if这将忽略 catcode 差异,并且只比较字符本身,因此它应该可以完成您要查找的操作。我标记了更改的两行。将此代码添加到您的序言中应该可以解决问题:

\makeatletter
\def\lst@CDefIt#1#2#3#4#5#6#7#8{%
    \ifx\@empty#2\@empty
        \def#1{#6\def\lst@next{#7#4#8}\lst@next}%
    \else \ifx\@empty#3\@empty
        \def#1##1{%
            #6%
            % \ifx##1#2\def\lst@next{#7#4#8}\else % <--- Before
            \if\noexpand##1\noexpand#2\def\lst@next{#7#4#8}\else % <--- After
                     \def\lst@next{#5##1}\fi
            \lst@next}%
    \else
        \def#1{%
            #6%
            \lst@IfNextCharsArg{#2#3}{#7#4#8}%
                                     {\expandafter#5\lst@eaten}}%
    \fi \fi}
\def\lst@IfNextChars@@#1#2\relax#3{%
    \def\lst@tofind{#2}\lst@lAddTo\lst@eaten{#3}%
    % \ifx#1#3% <--- Before
    \if\noexpand#1\noexpand#3% <--- After
        \ifx\lst@tofind\@empty
            \let\lst@next\@tempa
        \else
            \let\lst@next\lst@IfNextChars@
        \fi
        \expandafter\lst@next
    \else
        \expandafter\@tempb
    \fi}
\makeatother

1open如果你仔细阅读,你会发现这个解释是错误的,因为如果代码运行和close代码失败是同一件事,那么这毫无意义。好吧,确实没有意义 :) 我猜想open代码与close,因此一个可以运行,另一个则不能。说实话调试listings周日的,所以我假设部分代码有效,并且没有费力去寻找原因。

相关内容