2012-02-26 14 views
12

Algunos antecedentes, me siento cómodo con Emacs Lisp y he escrito muchas líneas. Sin embargo, nunca he escrito un modo principal, por lo que soy bastante nuevo sobre cómo funciona el mecanismo de bloqueo de fuentes.emacs: ¿hay un claro ejemplo de bloqueo de fuente multilínea?

Para mi proyecto actual, me gustaría agregar JavaScript incrustado y resaltado CSS a html-mode. Actualmente, hago esto con MMM-mode, pero es voluminoso y no uso otras características, así que me gustaría hacer un modo menor o incluso un truco que pueda agregar al sgml-mode-hook para simplemente hacer el resaltado.

He encontrado este section del manual, que a duras penas falta un ejemplo, y este emacswiki page de código interrumpido.

¿Puede alguien mostrarme un claro ejemplo de cómo se puede hacer esto?

EDITAR: Debería aclarar que no quiero ver el bloqueo de fuente específico del modo dentro de los fragmentos javascript/css. El único requisito es que pueda ver los trozos aplicando una cara diferente a ellos.

Respuesta

6

En el ejemplo siguiente, utilizo la forma "anclada" de las palabras clave font-lock, que le permite buscar más que la línea actual. El "truco" es que el gancho "pre" hace dos cosas: 1) le permite colocar el punto al inicio de la búsqueda y 2) le permite limitar la búsqueda al devolver la posición final. En el siguiente ejemplo, he usado la segunda propiedad.

Tenga en cuenta que esto es solo una prueba de concepto. Deberá asegurarse de que la variable font-lock-multiline y las palabras clave font-lock se apliquen al búfer correcto.

(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))))) 

A continuación, se resaltarán las dos primeras líneas del bar, pero no el último:

FOO BAR BAR BAR BAR 
BAR BAR BAR BAR 

BAR BAR BAR BAR 
+1

En este [hilo] (http://lists.gnu.org/archive/html/help-gnu-emacs/2004-09/msg00534.html), Stefan Monnier dice que font-lock-multi-line es apropiado para cosas que generalmente son de una sola línea, pero ocasionalmente de varias líneas. ¿Comentarios? –

+0

Bueno, Stefan Monnier es la referencia definitiva cuando se trata de algo relacionado con font-lock, por lo que cualquier consejo de él vale la pena seguirlo. Usar el sistema sintáctico, como él sugiere, es una forma de hacerlo. Otra es utilizar una "función de coincidencia" en lugar de una expresión regular, que podría coincidir con todo el bloque. Debería ver mis respuestas como una respuesta directa a la pregunta (proporcione un ejemplo de la función de bloqueo de fuentes de varias líneas) y tal vez no una respuesta sobre la mejor manera de lograr lo que sea que el cartel original le pregunte. – Lindydancer

3

Probablemente no sea el mejor ejemplo posible, pero se puede ver cómo haml-mode ha resuelto el problema de resaltado de sintaxis en regiones de submodo. Aquí está el blog post con una descripción de alto nivel.

Tenga en cuenta que el actual haml-mode tiene algunos problemas con la compatibilidad de Emacs 24, pero un par de horquillas tienen soluciones para esto.

En cuanto al bloqueo de fuente multilínea, creo que puede estar haciendo la pregunta incorrecta. Pero básicamente, esto resuelve el problema de qué hacer si el usuario ha realizado una edición en el medio o al final de una construcción sintáctica de líneas múltiples. Inicialmente, font-lock comienza a refontar el búfer desde la posición del punto. Los dos valores predeterminados font-lock-extend-region-functions, font-lock-extend-region-wholelines y font-lock-extend-region-multiline, mueven el inicio de la región de refontificación al principio de la línea, y luego tal vez en algún otro lugar, dependiendo de la propiedad font-lock-multiline. Si necesita que se mueva más hacia arriba, puede agregar otra función al font-lock-region-functions, o asegurarse de retroceder programáticamente mientras analiza ciertas construcciones, dentro de font-lock-region-function o syntax-propertize-function.

Un ejemplo del último enfoque sería Ruby's heredoc y ruby-syntax-propertize-heredoc en el tronco de Emacs. Se llama desde dos lugares en ruby-syntax-propertize-function. La primera vez que manejamos el caso cuando ya estamos dentro de un literal heredoc, y luego para cualquier heredocs posterior.

4

Esbozaré un modo principal simple para resaltar los bloques <style> (CSS) y <script> (JavaScript, etc.). Para obtener el bloqueo de fuente de varias líneas trabajando razonablemente bien, tendrá que primero habilitarla estableciendo font-lock-multiline-t y escribir una función para añadir a font-lock-extend-region-functions que se extenderá la región búsqueda relevante para contener grandes bloques de texto. Luego, necesitará escribir emparejamientos multilínea, ya sean expresiones o funciones regulares, y agregarlos al font-lock-defaults.

Aquí es una definición básica modo mayor que los nombres de la fuente bloqueo lista de palabras clave (en este caso, test-font-lock-keywords), permite bloqueo de fuente de múltiples líneas, y agrega la función de extensión región test-font-lock-extend-region.

función de extensión
(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)) 

La región debe ser algo como esto:

(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)))) 

Esta función analiza las variables globales font-lock-beg y font-lock-end, que contienen la posiciones inicial y final de la región de búsqueda y extiende la región para contener un bloque completo de texto (separados por líneas en blanco, o "\n\n").

Ahora que Emacs buscará coincidencias en regiones más grandes, debemos configurar la lista test-font-lock-keywords. Hay dos formas razonables de hacer coincidir construcciones multilínea: , una expresión regular que coincidirá entre líneas y una función correspondiente. Daré ejemplos de ambos. Esta lista de palabras clave contiene una expresión regular para hacer coincidir <style> bloques y una función para hacer coincidir <script> bloques:

(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.") 

El primer elemento de la lista es sencillo: una expresión regular y una cara para poner de relieve partidos de que el uso regular expresión. El segundo parece un poco más complicado, pero se puede generalizar para especificar diferentes caras para diferentes grupos definidos en los datos de coincidencia especificados por la función. Aquí, simplemente resaltamos grupo cero (la coincidencia completa) usando font-lock-keyword-face. (La documentación pertinente para estos comparadores se encuentra en la sección de Search-based fontification de el manual de Emacs.)

con una expresión regular para hacer coincidir <style> bloques serían:

(defconst test-style-block-regexp 
    "<style>\\(.\\|\n\\)*</style>" 
    "Regular expression for matching inline CSS blocks.") 

Tenga en cuenta que tenemos que poner \n en el grupo interno porque . no coincide con líneas nuevas.

La función de emparejamiento, por el contrario, tiene que buscar la primera <script> bloque en la región desde el punto de la única argumento dado, 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))) 

Esta función establece los datos de partido , que es una lista del formulario begin-0 end-0 begin-1 end-1 ... que indica el comienzo y el final del grupo zeroth, primer grupo, y así sucesivamente. Aquí, solo damos límites en el bloque completo que coincidió, pero podría hacer algo más sofisticado, como establecer diferentes caras para las etiquetas y el contenido .

Si se combinan todos este código en un solo archivo y ejecutar M-x test-mode, debería funcionar para poner de relieve estos dos tipos de bloques . Aunque creo que esto hace el trabajo, si hay una forma más eficiente o apropiada de hacerlo, también me gustaría saber .

Cuestiones relacionadas