我可以使用“listings”包中的“moredelim”来获取正则表达式吗?

我可以使用“listings”包中的“moredelim”来获取正则表达式吗?

我想知道是否可以使用该listings包突出显示正则表达式,以使它们看起来像普通的字符串对象。注意:正则表达式的语法不是上下文无关的,所以我不能指望一个能识别 100% 情况的解决方案。无论如何,例如在 Ruby 中,我想写

(/[a-z]+/)

并告诉使用左括号作为锚点来listings识别正则表达式(使其与算术除法区分开来):/[a-z]+/(

\lstset{moredelim=[s][\color{red}]{(/}{/}}

但是,这会使左括号变色 - 而不仅仅是正则表达式本身。为了更好地理解解析器,我将语句修改为:

 \lstset{moredelim=[s][\color{red}\textcolor{black}{:macro:}]{(/}{/}}

然而令我惊讶的是,“:macro:”被应用于每个标记:(/[a-z]/,由 发现moredelim。我错误地认为“:macro:”会应用于全部的匹配表达式(/[a-z]+/。顺便说一下,如果我将语句更改为双星号版本,情况就会是这样:

 \lstset{moredelim=**[s][\color{red}\textcolor{black}{:macro:}]{(/}{/}}

但是,在这种情况下,正则表达式将通过表达式中的着色关键字进行解析listings。这不是我想要的。我只想将(其用作查找正则表达式的锚点,之后我想将锚点与正则表达式本身分开处理(赋予它不同的颜色)。

具体来说:我想知道我是否可以只挑选moredelim- 中包含(/- 的第一个标记并将其与其他标记分开处理。

这里提供了用于检查该问题的 MWE:

\documentclass{article}  
\usepackage[english]{babel}   
\usepackage{listings}  
\usepackage{xcolor} 

\lstnewenvironment{lstRuby}{  
 \lstset{  
  language={},  
  moredelim=[s][\color{red}\textcolor{black}{:macro:}]{(/}{/}  
 }  
}{}  

\begin{document}  
\setlength{\parindent}{0pt}  

\ttfamily  
This is the code:  

(/[a-z]+/)

This is what I want to achieve:

(\textcolor{red}{/[a-z]+/})

This is what I get using listings:

\begin{lstRuby}  
(/[a-z]+/)  
\end{lstRuby}  

\end{document}

答案1

这个答案可能对提问者来说已经不再重要了。但是因为这个问题比实际要难得多,而且可能对其他人也重要,所以我还是会发布这个答案。

正如问题和评论中提到的,该moredelim=**[s]变体将整个分隔文本视为一个单元/组,这允许在分隔组的开头和结尾(通过\aftergroup)插入任意文本。 问题是,分隔符之间的文本也应用了其他样式。 另一方面,moredelim=[s]不应用其他样式,而是将分隔符组样式应用于同一类(字母、其他等)的每个字符块。 这可以防止仅在分隔符之前和之后插入文本。

似乎没有一种简单的方法可以实现所需的效果,所以我们必须在这里挂钩一些内部机制。\lst@DelimOpen\lst@DelimClose宏控制在列表中找到新分隔符对时的操作。 因此,我们重新定义它们以安装两个钩子\@delim@open@hook\@delim@close@hook。 在这些中,我们可以检查当前处于活动状态的样式(通过与 进行比较\lst@currstyle),并据此选择适当的操作。 使用这种方法,可以并行使用几个这样的奇特分隔符。 请注意\@delim@close@hook执行输出最后一块字符,因此我们再次需要使用\aftergroup技巧将最后的动作移到分隔组中的最后一个字符之后。

最终实现用于moredelim=[is][\regexstyle]{(/}{/)}定义正则表达式的样式,其中字段i从输出中删除原始分隔符。\regexstyle是应用于正则表达式所有内部文本的实际样式。请确保在此处使用名称明确的包装器宏,否则测试\lst@currstyle可能会导致错误的结果。\regexstyle@start\regexstyle@end分别插入代码代替原始开始和结束分隔符的宏。

以下是完整示例:

\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}

\lstnewenvironment{lstRuby}{%
    \lstset{
        language={},
        moredelim=[is][\color{green}]{*}{*},
        moredelim=[is][\regexstyle]{(/}{/)},
        emphstyle=\color{blue},
        emph={foo}
    }%
}{}

\makeatletter

\let\orig@lst@DelimOpen=\lst@DelimOpen
\def\lst@DelimOpen#1#2#3#4#5#6\@empty{%
    \orig@lst@DelimOpen{#1}{#2}{#3}{#4}{#5}{#6}\@empty
    \@delim@open@hook
}
\let\orig@lst@DelimClose=\lst@DelimClose
\def\lst@DelimClose{%
    \@delim@close@hook
    \orig@lst@DelimClose
}

\def\@delim@open@hook{%
    \def\@temp{\regexstyle}%
    \ifx\lst@currstyle\@temp
        \regexstyle@start
    \fi
}
\def\@delim@close@hook{%
    \def\@temp{\regexstyle}%
    \ifx\lst@currstyle\@temp
        \aftergroup\regexstyle@end
    \fi
}

\def\regexstyle{\color{red}}
\def\regexstyle@start{({\regexstyle /}}
\def\regexstyle@end{{\regexstyle /})}

\makeatother

\begin{document}
\setlength{\parindent}{0pt}

\ttfamily
This is the code:

(/[a-z]+/)

This is what I want to achieve:

(\textcolor{red}{/[a-z]+/})

This is what I get using listings:

\begin{lstRuby}
text (/[a-z]+/) /[a-z]+/ *foo*
text foo (/foo|\/|foo/) *bar*
\end{lstRuby}

\end{document}

在此处输入图片描述

相关内容