¿Cómo puedo anidar una tabla de sintaxis en otra?

8

He escrito un modo simple para manejar JSON. Utiliza la maquinaria derivada para reutilizar la mayor parte del código de json-mode. Sin embargo, una adición es que puede insertar elisp en el texto JSON que se evalúa en el momento de envío JSON. Por ejemplo, un extracto de json se ve así:

{
    "parameters": {
        "IRC_USER": "stsquad",
        "PUB_KEY": `(format "\"%s\"" (s-trim (shell-command-to-string "cat ~/.ssh/id_rsa.pub")))`
    }
}

Actualmente, el resaltado de sintaxis de este texto está roto cuando el elisp arroja el resaltador de sintaxis JSON. Me gustaría configurar una tabla de sintaxis anidada para que elisp se reconozca correctamente como elisp dentro de los caracteres de escape (he elegido `en este caso). Entiendo que puedes unir char-tables (a partir de las cuales se construyen las tablas de sintaxis) con algo como:

(defvar lava-mode-syntax-table
  (let ((json-table (copy-syntax-table json-mode-syntax-table))
        (elisp-table (copy-syntax-table lisp-mode-syntax-table)))
    (set-char-table-parent elisp-table json-table)
    (modify-syntax-entry ?` "(`" json-table)
    (modify-syntax-entry ?` ")`" json-table)
    json-table)
  "LAVA Mode syntax table.
This is a combination of json-mode-syntax-table with an escape into
  lisp-mode-syntax table for the embedded elisp.")

Pero no entiendo cómo puedo modificar la tabla de sintaxis para comenzar a usar la tabla de sintaxis secundaria (elisp) mientras estoy entre los caracteres de escape.

stsquad
fuente
1
¿El resaltado de sintaxis es el único objetivo? Si es así, algunas reglas de bloqueo de fuente inteligente podrían ser mucho más fáciles que jugar con las tablas de sintaxis.
Malabarba
@Malabarba: principalmente, aunque sería bueno si los comandos de movimiento funcionaran como se esperaba en los bits lispy. Intenté jugar con font-lock pero no pude hacerlo funcionar correctamente: git.linaro.org/people/alex.bennee/ lava-mode.git / blob / HEAD: / ...
stsquad

Respuestas:

7

No desea anidar una tabla de sintaxis (que es una estructura vectorial) dentro de otra, desea configurar un búfer donde, dependiendo de la posición, se usaría una tabla de sintaxis en lugar de la otra.

La otra respuesta describe cómo hacer esto usando la syntax-tablepropiedad de texto. Aquí se explica cómo hacerlo utilizando uno de los paquetes de "modo principal múltiple", mmm-mode . Utilizará todo el modo primario en el nivel superior del búfer y la tabla de sintaxis del submodo, reglas de bloqueo de fuente, mapa de teclas, etc. en las "subregiones".

(require 'mmm-auto)
(setq mmm-global-mode 'auto)

(mmm-add-classes
 '((eljson :submode emacs-lisp-mode
           :front ": *\\(`\\)" :back "`"
           :front-match 1
           :face mmm-code-submode-face)))

(mmm-add-mode-ext-class 'json-mode "\\.el\\.json\\'" 'eljson)

Esto supone que sus archivos de modo mixto tienen nombre *.el.json. Ajustar según corresponda.

Ahora, instale mmm-mode, evalúe lo anterior y (solo entonces) abra uno de los archivos en cuestión.

Dmitry
fuente
Supongo que no he entendido completamente cómo funcionan las tablas de sintaxis. ¿Asumí que anidaron ya que debe haber un estado dependiente en los caracteres anteriores? Mi última experiencia de mmm-mode fue nxhtml-mode pero me pareció bastante torpe, por eso cambié a usar el modo web.
stsquad
nxhtml-modees bastante torpe, sí. mmm-modemenos, pero sigue siendo complejo. No estoy seguro si puedes hacer algo como esto en web-mode; pregunte a su autor, tal vez.
Dmitry
Las tablas de sintaxis no contienen ningún estado. Relacionado, el syntax-ppsscaché lo hace.
Dmitry
lo siento, aún no he aceptado la respuesta. No he tenido tiempo de mirar el modo mmm. No estoy seguro de si esto significa que debería volver a redactar la pregunta o dejar que continúe colgando.
stsquad
No me importa Pero si desea que presente una respuesta más completa usando el modo mmm, debe incluir el fragmento de ejemplo de su marcado doble en la pregunta.
Dmitry
7

Ok, vamos a aclarar algunos conceptos básicos.

Es posible anidar tablas de sintaxis

Las tablas de sintaxis no tienen que ser globales para todo el búfer. Puede aplicarlos como propiedades de texto a regiones específicas. Esto significa que de hecho puede aplicar la elisptabla de sintaxis solo a regiones rodeadas por backticks.

¿Cómo haces eso?

Aquí hay una manera de hacer eso. Este método lo hace inmediatamente antes de que el bloqueo de fuente se ejecute a través del búfer, por lo que debería evitar específicamente sus problemas de bloqueo de fuente.

(defun endless/set-syntax-then-fontify (beg end loudly)
  "Apply elisp syntax table to relevant regions before calling font-lock."
  (save-match-data
    (save-excursion
      (save-restriction
        (widen)
        (narrow-to-region beg end)
        (while (search-forward "`" nil 'noerror)
          ;; Using `end' here excludes the `, I don't know which syntax you
          ;; want to apply to that.
          (let ((left (match-end 0)))
            (when (search-forward "`" nil 'noerror)
              (add-text-properties
               left (match-beginning 0)
               (list 'syntax-table emacs-lisp-mode-syntax-table))))))))
  (font-lock-default-fontify-region beg end loudly))

En su definición de modo mayor, deberá agregar:

(set (make-local-variable 'font-lock-fontify-region-function)
     #'endless/set-syntax-then-fontify)

Las tablas de sintaxis no son lo mismo que el resaltado de sintaxis

El resaltador de sintaxis (el sistema de bloqueo de fuente) utiliza tablas de sintaxis como parte de su información, por lo que la solución anterior debería evitar que el resaltador se vuelva loco.

Sin embargo, eso es solo una parte de los datos, si también desea que el texto en los backticks tenga el mismo color que vería en un búfer de elisp, tendrá que extender la función anterior para hacerlo específicamente.

(defun endless/set-syntax-then-fontify (beg end loudly)
  "Apply elisp syntax table to relevant regions before calling font-lock."
  (save-match-data
    (save-excursion
      (save-restriction
        (widen)
        (narrow-to-region beg end)
        (while (search-forward "`" nil 'noerror)
          ;; Using `end' here excludes the `, I don't know which syntax you
          ;; want to apply to that.
          (let ((left (match-end 0)))
            (when (search-forward "`" nil 'noerror)
              (add-text-properties
               left (match-beginning 0)
               (list 'syntax-table emacs-lisp-mode-syntax-table))))))))
  (font-lock-default-fontify-region beg end loudly)
  ;; Do some specific elisp fontifying here
  (save-match-data
    (save-excursion
      (save-restriction
        (widen)
        (narrow-to-region beg end)
        (while (search-forward "`" nil 'noerror)
          (let ((left (match-end 0)))
            (when (search-forward "`" nil 'noerror)
              ;; Call some function to fontify elisp between `left' and (match-beginning 0)
              )))))))
Malabarba
fuente
¿puedes ser un poco mas especifico? ¿Debo anidar una llamada al bloqueo de fuente para colorear elisp?
stsquad
@stsquad He extendido la respuesta con un ejemplo de dónde puede comenzar para eso. Iba a proporcionar una solución completa, pero me encontré con un obstáculo tras otro, y finalmente llegué a la conclusión de que esta sería una pregunta completamente diferente.
Malabarba
@Malabarba Gracias por la información, pero su solución completa parece equivocada: las propiedades del texto son parte del texto del búfer, por lo que está modificando el búfer cada vez que se fontifica. Eso invalidará varios cachés y hará que Emacs consuma más CPU sin una buena razón. Al menos debe usar with-silent-modifications(como lo font-lock-default-fontify-regionhace), pero aún mejor sería mover este add-text-propertiesnegocio a syntax-propertize-function.
Dmitry