我想知道是否可以使用该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}