【问题标题】:emacs: is there a clear example of multi-line font-locking?emacs:有没有明确的多行字体锁定示例?
【发布时间】:2012-03-16 04:39:04
【问题描述】:

一些背景知识,我对 Emacs Lisp 很满意,并且已经写了很多行。但是我从来没有写过一个主要的模式,所以我对字体锁定机制的工作原理还很陌生。

对于我当前的项目,我想在html-mode 中添加内联的 javascript 和 css 突出显示。目前,我使用 MMM 模式执行此操作,但它很笨重而且我不使用它的其他功能,所以我只想制作一个小模式,甚至只是一个可以添加到 sgml-mode-hook 的 hack只是做突出显示。

我发现了这个手册的section,它非常缺乏一个例子,这个emacswiki page 的代码被破坏了。

谁能给我一个清晰的例子来说明如何做到这一点?

编辑:我应该澄清一下,我不想在 javascript/css 块中看到特定于模式的字体锁定。唯一的要求是我能够通过对它们应用不同的面来查看这些块。

【问题讨论】:

    标签: emacs elisp font-lock


    【解决方案1】:

    在下面的例子中,我使用了“anchored”形式的字体锁定关键字,它可以让你搜索多于当前行的内容。 “诀窍”是“pre”钩子做两件事:1)它允许您将点定位到搜索的开始位置和2)它允许您通过返回结束位置来限制搜索。在下面的示例中,我使用了第二个属性。

    请注意,这只是一个概念验证。您需要确保将 font-lock-multiline 变量和 font-lock 关键字应用于正确的缓冲区。

    (defun my-end-of-paragraph-position (&rest foo)
      "Return position of next empty line."
      (save-excursion
        (while (not (or (eobp)             ; Stop at end of buffer.
                        (and (bolp)        ; Or at an empty line.
                             (eolp))))
          (forward-line))
        (point)))
    
    (setq font-lock-multiline t)
    
    (font-lock-add-keywords nil
                            '(("^FOO"
                               (0 font-lock-keyword-face)  ;; Face for FOO
                               ("BAR"
                                (my-end-of-paragraph-position)
                                nil
                                (0 font-lock-variable-name-face)))))
    

    下面,BAR 的前两行将突出显示,但不是最后一行:

    FOO BAR BAR BAR BAR
    BAR BAR BAR BAR
    
    BAR BAR BAR BAR
    

    【讨论】:

    • thread 中,Stefan Monnier 说 font-lock-multi-line 适用于通常是单行但偶尔是多行的东西。评论?
    • 好吧,Stefan Monnier 是任何与字体锁定相关的权威参考,因此他的任何建议都值得关注。正如他所建议的,使用句法系统是一种方法。另一种是使用“匹配函数”而不是正则表达式,它可以匹配整个块。您应该将我的回答视为对问题的直接回答(提供多行字体锁定功能的示例),并且可能不是完成原始发帖人所询问的最佳方式的答案。
    • 锚定可能是多行字体锁定的不可靠解决方案。 the manual 的当前版本警告,'返回大于行尾的位置通常是一个坏主意' - 正如你的 pre-form 函数 my-end-of-paragraph-position 所做的那样 - '换句话说,anchored-matcher 搜索不应跨行。'
    • @MichaelAllan,我猜这是手册中的一个错误。字体锁定包含专门处理多行锚定匹配的代码(当font-lock-multiline 不为零时)请参阅font-lock-fontify-anchored-keywords。但是,需要非常小心地处理这个正确的问题——特别是当 font-lock 重新突出显示一个区域时,它应该被扩展以包括所有相关的行,请参阅font-lock-extend-region-functions
    • 手册中的一个错误,我认为你是对的。它确实对我有用。
    【解决方案2】:

    我将概述一个简单的主要模式,用于突出显示 <style> (CSS) 和 <script>(JavaScript 等)块。获取多行字体锁定 工作得相当好,您需要首先通过设置启用它 font-lock-multilinet 并编写一个要添加到的函数 font-lock-extend-region-functions 将扩展相关 搜索区域以包含更大的文本块。然后,你需要 编写多行匹配器——正则表达式或函数——和 将它们添加到font-lock-defaults

    这是一个命名字体锁的基本主模式定义 关键字列表(此处为test-font-lock-keywords),启用 多行字体锁定,并增加区域扩展功能 test-font-lock-extend-region.

    (define-derived-mode test-mode html-mode "Test"
      "Major mode for highlighting JavaScript and CSS blocks."
      ;; Basic font lock
      (set (make-local-variable 'font-lock-defaults)
           '(test-font-lock-keywords))
      ;; Multiline font lock
      (set (make-local-variable 'font-lock-multiline) t)
      (add-hook 'font-lock-extend-region-functions
                'test-font-lock-extend-region))
    

    区域扩展函数应该如下所示:

    (defun test-font-lock-extend-region ()
      "Extend the search region to include an entire block of text."
      ;; Avoid compiler warnings about these global variables from font-lock.el.
      ;; See the documentation for variable `font-lock-extend-region-functions'.
      (eval-when-compile (defvar font-lock-beg) (defvar font-lock-end))
      (save-excursion
        (goto-char font-lock-beg)
        (let ((found (or (re-search-backward "\n\n" nil t) (point-min))))
          (goto-char font-lock-end)
          (when (re-search-forward "\n\n" nil t)
            (beginning-of-line)
            (setq font-lock-end (point)))
          (setq font-lock-beg found))))
    

    此函数查看全局变量font-lock-begfont-lock-end,其中包含的开始和结束位置 搜索区域,并扩展该区域以包含整个块 文本(以空行分隔,或"\n\n")。

    现在 Emacs 将在更大的区域中搜索匹配项,我们 需要设置test-font-lock-keywords 列表。那里有两个 匹配多行结构的相当好的方法: 一个将跨行匹配的正则表达式和一个匹配 功能。我将举两个例子。此关键字列表包含 用于匹配 <style> 块和函数的正则表达式 用于匹配<script> 块:

    (defvar test-font-lock-keywords
      (list
       (cons test-style-block-regexp 'font-lock-string-face)
       (cons 'test-match-script-blocks '((0 font-lock-keyword-face)))
       )
      "Font lock keywords for inline JavaScript and CSS blocks.")
    

    列表中的第一项很简单:一个正则表达式 以及用于突出显示该正则表达式匹配的面孔。 第二个看起来有点复杂,但可以概括 为定义的不同组指定不同的面 匹配函数指定的数据。在这里,我们只强调 使用font-lock-keyword-face 将零组(整个匹配)分组。 (相关文件为 这些匹配器位于Search-based fontification 部分 Emacs 手册。)

    匹配<style> 块的基本正则表达式是:

    (defconst test-style-block-regexp
      "<style>\\(.\\|\n\\)*</style>"
      "Regular expression for matching inline CSS blocks.")
    

    请注意,我们必须将\n 放在内部组中,因为. 没有 匹配换行符。

    另一方面,匹配函数需要寻找第一个 &lt;script&gt; 块中从点到单个给定的区域 论据,last

    (defun test-match-script-blocks (last)
      "Match JavaScript blocks from the point to LAST."
      (cond ((search-forward "<script" last t)
             (let ((beg (match-beginning 0)))
               (cond ((search-forward-regexp "</script>" last t)
                      (set-match-data (list beg (point)))
                      t)
                     (t nil))))
            (t nil)))
    

    该函数设置匹配数据,为列表形式 begin-0 end-0 begin-1 end-1 ... 给出开头和结尾 第零组,第一组,依此类推。在这里,我们只给出界限 匹配的整个块,但你可以做更多的事情 复杂的,例如为标签设置不同的面和 内容。

    如果您将所有这些代码合并到一个文件中并运行 M-x test-mode,它应该可以突出这两种类型 块。虽然我相信这可以完成工作,但如果有更多 有效或适当的方法,我也很好奇 也知道。

    【讨论】:

    • font-lock-defaults 和 font-lock-multiline 在设置时自动为缓冲区本地 (Emacs 25.3)
    【解决方案3】:

    这可能不是最好的例子,但你可以看看haml-mode 是如何解决子模式区域中的语法高亮问题的。这是带有高级描述的blog post

    请注意,当前的 haml-mode 在 Emacs 24 兼容性方面存在一些问题,但有几个分支对此进行了修复。

    关于多行字体锁定,我想你可能问错了问题。但基本上,这解决了如果用户在多行句法结构的中间或末尾进行编辑时该怎么做的问题。最初,font-lock 从该点的位置开始重新字体化缓冲区。两个默认的font-lock-extend-region-functionsfont-lock-extend-region-wholelinesfont-lock-extend-region-multiline,将重新字体化区域的开头移动到行的开头,然后可能更远,这取决于font-lock-multiline 属性。如果您需要它进一步向上移动,您可以向font-lock-region-functions 添加另一个函数,或者确保在解析font-lock-region-functionsyntax-propertize-function 内部的某些结构时以编程方式回溯。

    后一种方法的一个例子是 Ruby 的 heredoc 和 Emacs 主干中的 ruby-syntax-propertize-heredoc。它是从ruby-syntax-propertize-function 的两个地方调用的。第一次处理我们已经在 heredoc 文本中的情况,然后处理任何后续的 heredocs。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-12
      • 1970-01-01
      • 2021-08-05
      • 2011-04-28
      • 2017-05-14
      • 1970-01-01
      相关资源
      最近更新 更多