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.

Другие вопросы по тегам