在列表中使用单词作为分隔符时出现意外行为

在列表中使用单词作为分隔符时出现意外行为

我正在尝试自定义列表样式,以便自动突出显示我的代码,使其与 Eclipse/PyDev 安装完全相同(或足够接近)。我希望能够使用“class”和“:”作为分隔符集,这样“class”和“:”之间的任何内容都将以某种方式格式化。具体来说,我希望“class”具有“关键字”颜色,“class”和“:”之间的所有内容都是“classc”颜色或从其他定义继承其格式(“Base”应为“classc”颜色,“(object)”应为“code”颜色)。

我尝试通过实现一个宏来实现这一点,该宏重新格式化分隔符之间的文本,同时以不同的格式重新打印分隔符。

给出的输出的第一行几乎完全符合我的要求,只是出于某种原因,“:”出现在 Base 和 (object) 之间,而不是 (object) 之后。也就是说,颜色是正确的,但该行应该是“class Base(object):”。

第二行输出的缩进似乎导致了另一个问题。同样,这里的颜色基本正确,但我希望输出为“def new_function(self):”。从技术上讲,我也希望“self”具有“关键字”颜色,但我认为那是另一个问题。

\documentclass{article}
\usepackage{listings, color}

\definecolor{keyword}{RGB}{221,40,103}
\definecolor{classc}{RGB}{18,144,195}
\definecolor{code}{RGB}{217,232,247}
\definecolor{bg}{RGB}{35,35,35}
\definecolor{function}{RGB}{167,236,33}

\newcommand{\classHighlight}[1]{{\ttfamily\color{keyword}class\ }{\bfseries\color{classc}#1}{:}}

\newcommand{\functionHighlight}[1]{{\ttfamily\color{keyword}def\ }{\bfseries\color{function}#1}{:}}

\lstdefinestyle{myPython}{
    language = Python,
    basicstyle = \color{code}\ttfamily,
    morekeywords=[1]{self,None,True,False,class,def},
    moredelim={*[is][\classHighlight]{class\ }{:}},
    moredelim={*[is][\functionHighlight]{def\ }{:}},
    moredelim={*[s][\color{code}]{(}{)}},
    backgroundcolor = \color{bg},
}
\lstset{style=myPython}

\begin{document}
    \begin{lstlisting}
class Base(object):
    def new_function(self):
        function contents
    \end{lstlisting}
\end{document}

输出

当我删除“i”标签并运行以下几行时,我看到了令我更加困惑的输出:

moredelim={*[s][\classHighlight]{class\ }{:}},
moredelim={*[s][\functionHighlight]{def\ }{:}},

在此处输入图片描述

答案1

因此,问题在于 listings 认为 delimiter styler 命令不会打印除其参数之外的任何内容,因此它认为重复调用该命令来排版不同部分的代码是安全的。您已告知 styler 实际打印文本,因此如果多次调用该命令,文本将被打印多次。

准确地说,它被调用了两次或三次。考虑如下代码:

    class Base(object):

\classHighlight被调用三次。第一次调用时,它的参数是一个空格,它应该进行缩进(我不确定为什么需要在空格上调用它——在我看来,缩进的样式应该与行上的内容无关,因此这对我来说似乎是一个错误)。

第二次调用时,它会获取参数Base,因此此时我们要使用适当的颜色打印classBase。如果您正确设置了分隔符,\classHighlight则会使用参数 第三次调用:

那么我们该如何处理这个问题呢?我们不能每次都让宏打印“class”,否则“class”会出现在三个地方。我们要做的就是测试我们是否传递了空格命令,是否传递了 ,或者只是其他:东西。如果我们传递了一个空格或冒号,就按原样输出。如果我们传递了其他东西,就按需要着色。

唯一重要的细节是我们需要制作结束分隔符,):而不是仅仅:因为某种原因。还请注意,如果没有,这将彻底失败moredelim={*[s][\color{code}]{(}{)}}

完整代码如下:

\documentclass{article}
\usepackage{listings, color}


\makeatletter
% This macro makes the style commands
% #1 -- the name of the command, 
% #2 -- the styling code
\def\makeHighlightCommand#1#2{%
    \gdef#1##1{%
        \testkern{##1}% Check if ##1 starts with \kern, if so just output it
        {% else do this code
            \edef\testa{\the\lst@token}% The actual thing to be typeset is in \lst@token
            \def\testb{:}%
            \ifx\testa\testb % Test whether lst@token is just : 
                :% if so print the colon
                 % we're done now but for some reason \lst@currstyle (which is what 
                 % contains this function) doesn't go out of scope correctly.
                \global\let\lst@currstyle\relax % So we explicitly set it to \relax
            \else
                #2% Use the styling code
            \fi
        }%
    }%  
}  

% Tests if the first argument starts with \kern, 
% if so, just outputs its first argument, otherwise just outputs its second argument    
\def\testkern#1{\testkern@#1\relax\nil}
\def\testkern@#1#2\nil{\ifx#1\kern #1#2\expandafter\@gobble\fi}

% Uses listing's internal method to output a sequence of letters
% makes them super far apart like all the other letters
\def\lsttt#1{%
    \bgroup
    \let\lst@currstyle\relax
    \lsttt@#1.
    \egroup
}
\def\lsttt@#1{\ifx#1.\else\lst@length=1\lst@token{#1}\lst@Output\expandafter\lsttt@\fi}
\makeatother

\definecolor{keyword}{RGB}{221,40,103}
\definecolor{classc}{RGB}{18,144,195}
\definecolor{code}{RGB}{217,232,247}
\definecolor{bg}{RGB}{35,35,35}
\definecolor{function}{RGB}{167,236,33} 


\makeHighlightCommand\classHighlight{{\ttfamily\color{keyword}\lsttt{class}}{\bfseries\color{classc}#1}}
\makeHighlightCommand\functionHighlight{{\ttfamily\color{keyword}\lsttt{def}}{\bfseries\color{function}#1}}
\lstdefinestyle{myPython}{
    language = Python,
    basicstyle = \color{code}\ttfamily,
    morekeywords=[1]{self,None,True,False,class},
    moredelim={*[is][\classHighlight]{class\ }{):}},
    moredelim={*[is][\functionHighlight]{def\ }{):}},
    moredelim={*[s][\color{code}]{(}{)}},
    backgroundcolor = \color{bg},
}
\lstset{style=myPython}

\begin{document}
\begin{lstlisting}
class Base(object):
    def new_function(self):
         contents
\end{lstlisting}

\end{document}

输出如下:

在此处输入图片描述

如果这没有按照你想要的方式进行,请告诉我。

编辑:Kevin 抱怨关键字 def 和 class 的字母之间的间距与其他所有字母之间的间距不一样。为了解决这个问题,我添加了代码:

\def\lsttt#1{%
    \bgroup
    \let\lst@currstyle\relax
    \lsttt@#1.
    \egroup
}
\def\lsttt@#1{\ifx#1.\else\lst@length=1\lst@token{#1}\lst@Output\expandafter\lsttt@\fi}

并用 包裹起来class。现在间距一致,它看起来如下所示: deflsttt在此处输入图片描述

相关内容