¿Por qué insertar un resaltado de salto de nueva línea con la función de propiedad de sintaxis?

6

Estoy tratando de escribir un modo mayor que resalte las cadenas entre comillas triples. Aquí hay un ejemplo mínimo reproducible:

(defconst demo-triple-quoted-string-regex
  (rx "\"\"\""
      ;; After the delimiter, we're a sequence of
      ;; non-backslashes or blackslashes paired with something.
      (*? (or (not (any "\\"))
              (seq "\\" anything)))
      "\"\"\""))

(defun demo-stringify-triple-quote ()
  "Put `syntax-table' property on triple-quoted strings."
  (let* ((string-literal (match-string 0))
         (string-start-pos (- (point) (length string-literal)))
         (string-end-pos (point)))
    (unless (nth 4 (syntax-ppss)) ;; not inside comment
      (put-text-property string-start-pos string-end-pos
                         'syntax-table (string-to-syntax "|")))))

(defconst demo-syntax-propertize-function
  (syntax-propertize-rules
   (demo-triple-quoted-string-regex
    (0 (ignore (demo-stringify-triple-quote))))))

(define-derived-mode demo-mode prog-mode "Demo"
  "Major mode showing stack overflow question."
  (set (make-local-variable 'font-lock-defaults) '(()))
  (set (make-local-variable 'syntax-propertize-function)
       demo-syntax-propertize-function))

Sin embargo, esto conduce a un comportamiento realmente extraño al modificar el búfer. Aquí está mi contenido de búfer:

dodgy when we put a newline after babel

"""
a
"
babel

"""

x = 1

M-x demo-mode da resaltado correcto:

demo-before

pero presionar enter de repente da esto:

demo-after

¿Qué estoy haciendo mal?

Wilfred Hughes
fuente
No tengo una solución, pero noté el mismo problema con la continuación de cursiva y negrita en los ajustes de línea en el modo org.
Usuario de Emacs
1
El primer problema es que está poniendo la sintaxis en todos los caracteres de la cadena, mientras que solo debe hacerse para el primer y el último carácter de valla. Puede verificar que Emacs trata cada par de su supuesta cadena como un sexp, es decir, una sola cadena, a través de forward-sexp.
politza
1
El segundo problema es que realmente no puedes unir las cuerdas como imaginas. Esto solo funcionaría si se garantiza que la búsqueda comience fuera de cualquier cadena ya presente en el búfer. Después de todo, son pares iguales: un triple comienza una cadena si y solo si está precedida por un número par de otros triples. Afortunadamente hace syntax-ppssun seguimiento de esto. Echa un vistazo a cómo se hace python.el.
politza
@politza ¡Estoy asombrado de tus habilidades de elisp! Muchas gracias :). Sus correcciones fueron suficientes para que mi código funcionara (vea la respuesta a continuación), por lo que ahora puedo corregir el error del modo julia que me metió en esto.
Wilfred Hughes

Respuestas:

3

Gracias a Politza, y avanzando python-syntax-stringifycon edebug, tengo esto funcionando. Los cambios fueron:

  • | solo debe aplicarse al primer y último carácter en la cadena de comillas triples.
  • (¿Porque el análisis sintáctico es incremental?) No es posible buscar una cadena completa. En su lugar, busque un delimitador y vea si está o no en una cadena entre comillas triples.

Código de trabajo:

(defconst demo-triple-quoted-string-regex
  (rx "\"\"\""))

(defun demo-stringify-triple-quote ()
  "Put `syntax-table' property on triple-quoted strings."
  (let* ((string-end-pos (point))
         (string-start-pos (- string-end-pos 3))
         (ppss (prog2
                   (backward-char 3)
                   (syntax-ppss)
                 (forward-char 3))))
    (unless (nth 4 (syntax-ppss)) ;; not inside comment
      (if (nth 8 (syntax-ppss))
          ;; We're in a string, so this must be the closing triple-quote.
          ;; Put | on the last " character.
          (put-text-property (1- string-end-pos) string-end-pos
                             'syntax-table (string-to-syntax "|"))
        ;; We're not in a string, so this is the opening triple-quote.
        ;; Put | on the first " character.
        (put-text-property string-start-pos (1+ string-start-pos)
                           'syntax-table (string-to-syntax "|"))))))

(defconst demo-syntax-propertize-function
  (syntax-propertize-rules
   (demo-triple-quoted-string-regex
    (0 (ignore (demo-stringify-triple-quote))))))

(define-derived-mode demo-mode prog-mode "Demo"
  "Major mode showing stack overflow question."
  (set (make-local-variable 'font-lock-defaults) '(()))
  (set (make-local-variable 'syntax-propertize-function)
       demo-syntax-propertize-function))
Wilfred Hughes
fuente
¿Por qué calculas ppssen let*y luego nunca usas ese valor?
Michael Norrish el