我将概述一种用于突出显示<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))
区域扩展函数应如下所示:
(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-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.")
列表中的第一项很简单:一个正则表达式和一个用于突出显示该正则表达式匹配项的面孔。第二种看起来有点复杂,但可以概括为为函数指定的匹配数据中定义的不同组指定不同的面。在这里,我们只使用 突出显示第 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
,它应该可以突出显示这两种类型的块。虽然我相信这可以完成这项工作,但如果有更有效或更合适的方法来解决它,我也很想知道。