【问题标题】:special non-syntactic highlighting in PygmentsPygments 中特殊的非句法突出显示
【发布时间】:2019-03-06 10:31:27
【问题描述】:

我正在使用 Pygments 作为 MarkDown、Python 和 LaTeX 的一部分(通过minted)。由于这些原因,我无法轻易更换到其他工具,并希望在 Pygments 中找到解决方案。

我想要实现的是超越普通语法突出显示的“特殊突出显示”。例如,这包括将特定终端输出保持其原始颜色。或者添加额外的样式以更轻松地突出显示参数供用户填写。一个示例肯定会让这更容易理解。

在 Apple 编写的 Swift 手册中,供您填写的参数在气球中突出显示。这使它们更容易识别,并且气球可以覆盖一系列标记:

我想用 Pygments 实现类似的东西。仅仅改变样式是行不通的。当这些东西不在气球内时,您希望这些东西通常按照它们的标记类型设置样式,并且当它在气球内时,您不希望任何类型的特定突出显示。我不认为这些类型的气球在当前的香草 Pygments 中是可能的。我认为我需要一种新型令牌来实现我想要的。我想将它用于各种语言,所以理想情况下,我想找到一种方法来用最少的修改来做到这一点。可悲的是,我不认为我可以使用 Pygments 过滤器实现我想要的。我的直觉是我需要为我想使用的所有语言重写词法分析器,但我认为我可以通过子类化它们来做到这一点。

总而言之,我想要的是我提供代码,例如

§label name§: while §condition§ {

并且,例如在 HTML 输出中,类似于

<pre><span class="balloon">label name</span>: <span class="k">while</span><span class="balloon">condition</span> { </pre>

出来。我并没有将§ 符号用于这个新环境或类似的东西,这只是预期行为的一个例子。

由于为我使用的每种语言修改词法分析器是一项艰巨的任务,而且由于 Pygments 的文档非常简陋,所以我想问一下如何解决这个问题以及我是否忽略了某些东西。我是否正确地假设我需要一个新令牌并且需要重写每个词法分析器?还是有更聪明的方法?

【问题讨论】:

  • 我认为这个问题可能“太宽泛”所以只是评论:我不认为你想要/不需要替换输出中的类。只需在跨度 (&lt;span class="k balloon"&gt;) 上添加 balloon 作为第二类。为什么不使用 HTML 过滤器来传递 Pygments 输出呢?过滤器可以识别特殊元素并相应地修改它们。我想你可以用一些额外的字符来识别要修改的标记,但是为什么不使用 Pygments 用于突出显示行的相同方法呢?传递一个列表:filter(html, ['label name', 'condition']).
  • 这很有趣,谢谢。正如您所提到的,可能确实不需要添加第二个类。尽管如此,Pygments 内部需要能够区分不同种类的标记(我不太了解 LaTeX 输出的工作原理)。您对突出显示的建议是一个很好的建议。这可能正是需要的,也可能正是我正在寻找的那种输入。明天我会调查一下,看看是否有用。
  • 我试了一下,过滤器不起作用。不过,您的建议确实导致了解决方案。我首先开始对现有的词法分析器进行子类化并添加一个过滤器,但后来对代码进行了几次迭代,似乎可以通过使用单行代码逐个子类化所需的语言来实现这一点。我会将其发布为答案,尽管我希望提供更多输入以查看是否还有更好的方法。

标签: python-3.x latex markdown pygments


【解决方案1】:

感谢 Waylan 的评论,我已走上正轨并找到了可行的解决方案:

from pygments import token
from pygments.lexer import inherit
from pygments.lexers import Python3Lexer, SwiftLexer


# create a new token and associate with it a new short style name
token.STANDARD_TYPES[token.Token.Balloon] = "balloon"
Balloon = token.Token.Balloon


# create a callback which ignores the special § characters
def process_balloon(_, match):
    yield match.start(), Balloon, match.group(1)


# subclass the Python 3 lexer to identify ballooned text
# when subclassing, the tokens dictionaries will be merged
class Python3BalloonLexer(Python3Lexer):
    tokens = {
        "root": [(r'§(.*?)§', process_balloon),  # balloons are processed first
                 inherit]  # and then merge the superclass tokens here
    }

# do the same for the Swift language with a one-liner
class SwiftBalloonLexer(SwiftLexer):
    tokens = { "root": [(r'§(.*?)§', process_balloon), inherit] }


# example output below
from pygments import highlight
from pygments.formatters import HtmlFormatter
my_lexer_python = Python3BalloonLexer()
my_lexer_swift = SwiftBalloonLexer()
print(highlight('print(§"Hello World"§)', my_lexer_python, HtmlFormatter()))
print(highlight('§label name§: while §condition§ {\n  §statements§\n}', 
      my_lexer_swift, HtmlFormatter()))

简而言之,此解决方案创建一个新令牌以确保与现有令牌没有冲突。然后,对于需要气球的每种语言,对该语言进行子类化以添加对新令牌的支持。此标记由回调函数处理,该函数仅获取 § 符号之间的内容。一旦找到所有气球,正常的词法分析器就会接管并处理语言的其余部分。

除了需要对每种语言逐一进行子类化之外,此解决方案似乎符合大多数情况。这有点麻烦,但问题不大。该方法仍然有点脆弱,因为它假设 § 仅用于突出显示这些气球。

可以采取类似的方法来强制例如文本必须始终具有相同颜色的特定样式,只需通过子类化添加/堆叠进一步的词法分析器扩展。当然,这会增加潜在冲突的风险,因为这意味着添加更多 特殊 符号,例如 §,这些符号可能已经在代码的其他位置使用。

【讨论】:

    猜你喜欢
    • 2012-08-24
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    相关资源
    最近更新 更多