Por que inserir uma quebra de nova linha realçando com a função sintaxe-propertize?

6

Estou tentando escrever um modo principal que destaque seqüências de caracteres triplas. Aqui está um exemplo mínimo reproduzível:

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

No entanto, isso leva a um comportamento realmente bizarro ao modificar o buffer. Aqui está o meu conteúdo do buffer:

dodgy when we put a newline after babel

"""
a
"
babel

"""

x = 1

M-x demo-mode dá realce correto:

demo-before

mas pressionar enter de repente dá o seguinte:

demo-after

O que estou fazendo errado?

Wilfred Hughes
fonte
Não tenho uma solução, mas notei o mesmo problema com a continuação de itálico e negrito nas linhas em modo org.
Emacs usuário
11
O primeiro problema é que você está colocando a sintaxe em todos os caracteres da string, enquanto isso deve ser feito apenas para o primeiro e o último caracteres da cerca. Você pode verificar se o Emacs trata cada par de sua suposta string como um sexp, ou seja, uma única string, via forward-sexp.
politza 23/06
11
O segundo problema é que você não pode realmente combinar as cordas da maneira que imagina. Isso funcionaria apenas se a pesquisa fosse garantida para iniciar fora de qualquer sequência já presente no buffer. Afinal, você está combinando pares: um triplo inicia uma sequência se, e somente se, é precedida por um número par de outras triplas. Felizmente, syntax-ppssacompanha isso. Veja como é feito python.el.
politza 23/06
@politza Estou admirado com suas habilidades no elisp! Muito obrigado :). Suas correções foram suficientes para que meu código funcionasse (veja a resposta abaixo), para que eu possa corrigir o bug no modo julia que me levou a isso.
Wilfred Hughes

Respostas:

3

Graças a Politza, e avançando python-syntax-stringifycom o edebug, eu tenho esse trabalho. As mudanças foram:

  • | deve ser aplicado apenas ao primeiro e ao último caractere na cadeia de caracteres tripla.
  • (Como a análise de sintaxe é incremental?) Não é possível procurar uma sequência inteira. Em vez disso, procure um delimitador e veja se você está ou não em uma sequência de aspas triplas.

Código de trabalho:

(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
fonte
Por que você calcular ppssna let*e depois nunca mais usar esse valor?
Michael Norrish 31/10/19