14

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

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

我找到了手册的这一部分,它非常缺乏一个例子,以及这个 emacswiki页面的损坏代码。

有人可以向我展示如何做到这一点的明确示例吗?

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

4

3 回答 3

10

在下面的示例中,我使用“锚定”形式的字体锁定关键字,它允许您搜索多于当前行的内容。“诀窍”是“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
于 2012-02-26T20:15:54.047 回答
9

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

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

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

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

请注意,我们必须放入\n内部组,因为.不匹配换行符。

另一方面,匹配函数需要 <script>在从点到单个给定参数的区域中查找第一个块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,它应该可以突出显示这两种类型的块。虽然我相信这可以完成这项工作,但如果有更有效或更合适的方法来解决它,我也很想知道。

于 2013-03-06T05:32:27.047 回答
4

这可能不是最好的示例,但您可以查看haml-mode如何解决子模式区域中的语法高亮问题。这是带有高级描述的博客文章。

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

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

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

于 2012-02-26T20:40:34.533 回答