有一些背景,我对Emacs Lisp很熟悉,写了很多行。但是我从来没有写过主模式,所以我对字体锁定机制的工作原理还很陌生。
对于我当前的项目,我想将内联javascript和css高亮显示添加到html-mode
中。目前,我使用MMM模式来完成这项工作,但它体积庞大,而且我不使用它的其他功能,所以我只想创建一个次要模式,甚至只是一个破解,我可以将其添加到sgml-mode-hook
中以进行高亮显示。
我已经找到了手册的这一部分,它非常缺乏一个例子,以及这一页破损的代码。
有人能给我举一个如何做到这一点的明确例子吗?
EDIT:我应该澄清一下,我不想在javascript/css块中看到特定于模式的字体锁定。唯一的要求是,我能够通过对它们应用不同的面来看到块。
我将概述一个简单的主模式,用于突出显示<style>
(CSS)和<script>
(JavaScript等)块。获取多行字体锁定工作得相当好,您需要首先通过设置font-lock-multiline
到t
,并编写要添加到的函数font-lock-extend-region-functions
将扩展相关搜索区域以包含较大的文本块。然后,你需要编写多行匹配器——正则表达式或函数——以及将它们添加到CCD_ 8中。
以下是命名字体锁的基本主模式定义关键字列表(此处为test-font-lock-keywords
),启用多行字体锁定,并添加区域扩展功能CCD_ 10。
(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 "nn" nil t) (point-min))))
(goto-char font-lock-end)
(when (re-search-forward "nn" nil t)
(beginning-of-line)
(setq font-lock-end (point)))
(setq font-lock-beg found))))
此函数查看全局变量font-lock-beg
和font-lock-end
,包含搜索区域,并扩展该区域以包含整个块文本(由空行分隔或"nn"
)。
现在Emacs将在更大的区域中搜索匹配项,我们需要设置CCD_ 14列表。有两个匹配多行构造的合理好方法:跨行匹配的正则表达式作用我将举两个例子。此关键字列表包含用于匹配CCD_ 15块的正则表达式和函数用于匹配<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.")
列表中的第一项很简单:正则表达式以及用于突出显示该正则表达式的匹配的面部。第二个看起来有点复杂,但可以概括为中定义的不同组指定不同的面匹配函数指定的数据。在这里,我们只强调使用CCD_ 17分组零(整个匹配)。(这些匹配器位于Emacs手册。)
用于匹配<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
,它应该可以突出显示这两种类型块。虽然我相信这能起到作用,但如果有更多我也很好奇我也知道。
在下面的例子中,我使用了字体锁定关键字的"锚定"形式,它允许您搜索比当前行更多的内容。"技巧"是"预"挂钩做两件事:1)它允许您将点定位到搜索的开始位置,2)它允许通过返回结束位置来限制搜索。在下面的示例中,我使用了第二个属性。
请注意,这只是概念的证明。您需要确保font-lock-multiline
变量和字体锁定关键字应用于正确的缓冲区。
(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
这可能不是最好的例子,但您可以看看haml模式是如何解决子模式区域中语法突出显示的问题的。这是一篇高级描述的博客文章。
请注意,当前的haml-mode
在Emacs24兼容性方面存在一些问题,但一些fork对此进行了修复。
关于多行字体锁定,我认为您可能问错了问题。但基本上,这解决了如果用户在多行语法构造的中间或结尾进行了编辑,该怎么办的问题。最初,字体锁定开始从点的位置重新确定缓冲区。两个默认的font-lock-extend-region-functions
,font-lock-extend-region-wholelines
和font-lock-extend-region-multiline
,将重新识别区域的起点移动到行的起点,然后可能移动到更远的地方,这取决于font-lock-multiline
属性。如果您需要它进一步向上移动,您可以向font-lock-region-functions
添加另一个函数,或者确保在解析font-lock-region-function
或syntax-propertize-function
中的某些构造时以编程方式回溯。
后一种方法的一个例子是Ruby的heredoc和Emacs主干中的ruby-syntax-propertize-heredoc
。它是从ruby-syntax-propertize-function
中的两个位置调用的。当我们已经在一个heredoc文本中时,第一次处理这个案例,然后再处理任何后续的heredoc。