emacs: есть ли явный пример многострочной блокировки шрифтов?
Немного предыстории, я доволен Emacs Lisp, и написал много строк об этом. Однако я никогда не писал основной режим, поэтому я довольно новичок в том, как работает механизм блокировки шрифтов.
Для моего текущего проекта я хотел бы добавить встроенный JavaScript и CSS подсветку html-mode
, В настоящее время я делаю это с MMM-режимом, но он громоздкий и я не использую другие его функции, поэтому я просто хотел бы сделать второстепенный режим или даже просто взлом, который я могу добавить к sgml-mode-hook
просто сделать выделение.
Я нашел этот раздел руководства, в котором очень не хватает примера, и эту страницу с неработающим кодом.
Может кто-нибудь показать мне наглядный пример того, как это можно сделать?
РЕДАКТИРОВАТЬ: я должен уточнить, что я не хочу видеть специфичную для режима блокировку шрифта в чанках javascript/css. Единственное требование - чтобы я мог видеть куски, применяя к ним другое лицо.
3 ответа
Я нарисую простой основной режим для выделения <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.")
Первый элемент в списке прост: регулярное выражение и лицо для выделения совпадений этого регулярного выражения. Второй выглядит немного сложнее, но его можно обобщить, чтобы указать разные грани для разных групп, определенных в данных соответствия, указанных в функции. Здесь мы просто выделяем ноль группы (полное совпадение), используя font-lock-keyword-face
, (Соответствующая документация для этих сопоставителей находится в разделе о фонтировании на основе поиска в руководстве по 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
, он должен работать для выделения этих двух типов блоков. Хотя я считаю, что это делает свою работу, если есть более эффективный или правильный способ сделать это, мне также было бы любопытно узнать также.
В приведенном ниже примере я использую "закрепленную" форму ключевых слов 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)))))
Ниже будут выделены первые две строки BAR, но не последние:
FOO BAR BAR BAR BAR
BAR BAR BAR BAR
BAR BAR BAR BAR
Вероятно, это не самый лучший пример, но вы можете посмотреть, как haml-mode решил проблему подсветки синтаксиса в подрежимных областях. Вот пост в блоге с описанием высокого уровня.
Обратите внимание, что текущий haml-mode
есть некоторые проблемы с совместимостью с Emacs 24, но пара вилок имеет исправления для этого.
Что касается многострочной блокировки шрифтов, я думаю, что вы, возможно, задаете не тот вопрос. Но в основном это решает проблему того, что делать, если пользователь сделал редактирование в середине или в конце многострочной синтаксической конструкции. Изначально font-lock начинает переопределять буфер с позиции точки. Два по умолчанию 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-syntax-propertize-heredoc
в багажнике Emacs. Это называется из двух мест в ruby-syntax-propertize-function
, Первый раз для обработки случая, когда мы уже внутри литерала heredoc, а затем для любых последующих heredocs.