Come posso annidare una tabella di sintassi in un'altra?


8

Ho scritto una modalità semplice per la gestione di JSON. Utilizza i macchinari derivati ​​per riutilizzare la maggior parte del codice della modalità json. Comunque un'aggiunta è che puoi inserire elisp nel testo JSON che viene valutato al momento dell'invio di JSON. Ad esempio un estratto di JSON è simile al seguente:

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

Attualmente l'evidenziazione della sintassi di questo testo è interrotta quando l'illuminatore della sintassi JSON viene lanciato dall'elisp. Vorrei impostare una tabella di sintassi nidificata in modo che elisp sia correttamente riconosciuto come elisp all'interno dei caratteri di escape (ho scelto `in questo caso). Capisco che puoi unire i char-table (da cui sono basate le tabelle di sintassi) con qualcosa del tipo:

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

Ma non capisco come posso modificare la tabella di sintassi per iniziare a usare la tabella di sintassi child (elisp) mentre si è tra i caratteri di escape?


1
La sintassi sta evidenziando l'unico obiettivo? In tal caso, alcune regole intelligenti per il blocco dei caratteri potrebbero essere molto più semplici che fare confusione con le tabelle di sintassi.
Malabarba,

@Malabarba: soprattutto anche se sarebbe bello se i comandi di movimento funzionassero come previsto nei bit lispy. Avevo provato a scherzare con font-lock ma non riuscivo a farlo funzionare correttamente: git.linaro.org/people/alex.bennee/ lava-mode.git / blob / HEAD: /…
stsquad

Risposte:


7

Non vuoi nidificare una tabella di sintassi (che è una struttura vettoriale) all'interno di un'altra, vuoi impostare un buffer in cui, a seconda della posizione, verrebbe utilizzata una tabella di sintassi anziché l'altra.

L'altra risposta descrive come fare ciò usando la syntax-tableproprietà text. Ecco come farlo usando uno dei pacchetti "multiple major mode", mmm-mode . Userà tutto nella modalità primaria al livello più alto del buffer e la tabella di sintassi della sottomodalità, le regole di blocco dei caratteri, la mappa dei tasti, ecc. Nelle "sottoregioni".

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

Ciò presuppone che i file in modalità mista siano denominati *.el.json. Regola come appropriato.

Ora installa mmm-mode, valuta quanto sopra e (solo allora) apri uno dei file in questione.


Immagino di non aver completamente compreso il modo in cui funzionano le tabelle di sintassi. Ho pensato che fossero nidificati in quanto ci doveva essere stato dipendente dai caratteri precedenti? La mia ultima esperienza con la modalità mmm è stata la modalità nxhtml ma l'ho trovata piuttosto goffa ed è per questo che sono passato all'utilizzo della modalità web.
stsquad,

nxhtml-modeè piuttosto goffo, sì. mmm-modemeno, ma è ancora complesso. Non sono sicuro che puoi fare qualcosa del genere in web-mode; chiedi al suo autore, forse.
Dmitry

Le tabelle di sintassi non contengono alcuno stato. Allo stesso modo, la syntax-ppsscache lo fa.
Dmitry

scusa non ho ancora accettato la risposta. Non ho avuto il tempo di guardare la modalità mmm. Non sono sicuro che ciò significhi che dovrei riformulare la domanda o lasciarla continuare?
stsquad,

Non mi dispiace Ma se desideri che io presenti una risposta più completa usando la modalità mmm, dovresti includere nella domanda lo snippet di esempio del tuo doppio markup.
Dmitry

7

Ok, chiariamo alcune nozioni di base.

È possibile annidare le tabelle di sintassi

Le tabelle di sintassi non devono essere globali per l'intero buffer. Puoi applicarli come proprietà di testo a regioni specifiche. Ciò significa che puoi effettivamente applicare la elisptabella di sintassi solo alle regioni circondate da backtick.

Come si fa a farlo?

Ecco un modo per farlo. Questo metodo lo fa immediatamente prima dell'esecuzione del blocco dei caratteri attraverso il buffer, quindi dovrebbe prevenire specificamente i problemi di blocco dei caratteri.

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

Nella definizione della modalità principale, dovrai aggiungere:

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

Le tabelle di sintassi non sono le stesse dell'evidenziazione della sintassi

L'evidenziatore di sintassi (il sistema di blocco dei caratteri) utilizza le Tabelle di sintassi come parte delle sue informazioni, quindi la soluzione sopra dovrebbe impedire all'evidenziatore di impazzire.

Tuttavia, questa è solo una parte dei dati, se vuoi anche che il testo nei backtick sia colorato esattamente come vedresti in un buffer elisp, dovrai estendere la funzione sopra per farlo in modo specifico.

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

puoi essere un po 'più specifico? Devo nidificare una chiamata per bloccare il carattere per eseguire la colorazione elisp?
stsquad,

@stsquad Ho esteso la risposta con un esempio di dove puoi iniziare. Stavo per fornire una soluzione completa, ma mi sono imbattuto in un ostacolo dopo l'altro e alla fine sono giunto alla conclusione che questa sarebbe stata tutta un'altra domanda.
Malabarba,

@Malabarba Grazie per le informazioni, ma la tua soluzione completa sembra sbagliata: le proprietà del testo fanno parte del testo del buffer, quindi stai modificando il buffer ogni volta che viene classificato. Ciò invaliderà diverse cache e farà sì che Emacs consumi più CPU senza una buona ragione. Dovresti almeno usare with-silent-modifications(come font-lock-default-fontify-regionfa), ma ancora meglio sarebbe spostare questo add-text-propertiesbusiness in syntax-propertize-function.
Dmitry,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.