eve*_*_jr 13 emacs elisp font-lock
有些背景,我对Emacs Lisp很满意,并写了很多行.但是我从来没有写过主要模式,所以我对字体锁定机制的工作方式还不熟悉.
对于我目前的项目,我想添加内联的javascript和css突出显示html-mode.目前,我这样做是有MMM的模式,但它的体积庞大,我不使用它的其他功能,所以我只想做一个小的模式,甚至只是一个黑客工具,我可以添加到sgml-mode-hook只是做突出显示.
我发现本手册的这一部分非常缺乏一个例子,这个破坏代码的emacswiki 页面.
有人能告诉我一个如何做到这一点的明确例子吗?
编辑:我应该澄清,我不希望在javascript/css块中看到特定于模式的字体锁定.唯一的要求是我可以通过对它们应用不同的面来看到块.
在下面的示例中,我使用font-lock关键字的"锚定"形式,它允许您搜索超过当前行."技巧"是指"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)))))
Run Code Online (Sandbox Code Playgroud)
下面,将突出显示BAR的前两行,但不是最后一行:
FOO BAR BAR BAR BAR
BAR BAR BAR BAR
BAR BAR BAR BAR
Run Code Online (Sandbox Code Playgroud)
我将概述一个突出显示<style>(CSS)和
<script>(JavaScript等)块的简单主要模式.为了得到多字体锁的工作还算不错,你需要通过设置首先打开这个
font-lock-multiline来t写一个函数添加到
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))
Run Code Online (Sandbox Code Playgroud)
区域扩展功能应如下所示:
(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))))
Run Code Online (Sandbox Code Playgroud)
此功能着眼于全局变量font-lock-beg和
font-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.")
Run Code Online (Sandbox Code Playgroud)
列表中的第一项是直截了当的:正则表达式和用于突出显示该正则表达式匹配的面.第二个看起来有点复杂,但可以推广到为函数指定的匹配数据中定义的不同组指定不同的面.在这里,我们只使用突出显示组零(整个匹配)font-lock-keyword-face.(这些匹配器的相关文档位于Emacs手册的基于搜索的fontification部分.)
匹配<style>块的基本正则表达式是:
(defconst test-style-block-regexp
"<style>\\(.\\|\n\\)*</style>"
"Regular expression for matching inline CSS blocks.")
Run Code Online (Sandbox Code Playgroud)
请注意,我们必须放入\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)))
Run Code Online (Sandbox Code Playgroud)
此函数设置匹配数据,该列表是begin-0 end-0 begin-1 end-1 ...给出第0组,第一组等的开头和结尾的表单列表
.在这里,我们只给出匹配的整个块的边界,但是你可以做一些更复杂的事情,比如为标签和内容设置不同的面.
如果将所有这些代码组合到一个文件中并运行
M-x test-mode,它应该可以突出显示这两种类型的块.虽然我相信这可以胜任这项工作,但如果有更有效或更恰当的方法,我也很想知道.
这可能不是最好的示例,但您可以看看haml-mode如何解决子模式区域中语法突出显示的问题。这是带有高级描述的博客文章。
请注意,当前的haml-modeEmacs 24 兼容性存在一些问题,但几个分支已修复此问题。
关于多行字体锁定,我认为您可能问错了问题。但基本上,这解决了如果用户在多行语法结构的中间或末尾进行编辑该怎么办的问题。最初,字体锁定从该点的位置开始重新格式化缓冲区。两个默认的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 和ruby-syntax-propertize-heredocEmacs 主干中的。它是从 中的两个地方调用的ruby-syntax-propertize-function。当我们已经处于定界文档文字内部时,第一次处理这种情况,然后处理任何后续的定界文档。