Bagaimana saya bisa menumpuk satu tabel sintaks di yang lain?

8

Saya telah menulis mode sederhana untuk menangani JSON. Ia menggunakan mesin turunan untuk menggunakan kembali sebagian besar kode json-mode. Namun satu tambahan adalah Anda dapat memasukkan elisp ke dalam teks JSON yang dievaluasi pada waktu pengiriman JSON. Sebagai contoh kutipan dari json terlihat seperti ini:

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

Saat ini penyorotan sintaks dari teks ini rusak karena sintaks JSON hightlighter get's dilemparkan oleh elisp. Saya ingin mengatur tabel sintaks bersarang sehingga elisp dikenali dengan benar sebagai elisp ketika di dalam karakter escape (saya telah memilih `dalam kasus ini). Saya mengerti Anda dapat bergabung dengan char-tables (dari mana sintaks-tables dibuat) dengan sesuatu seperti:

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

Tapi saya tidak mengerti bagaimana saya bisa memodifikasi tabel sintaks untuk mulai menggunakan tabel sintaks anak (elisp) sementara di antara karakter escape?

stsquad
sumber
1
Apakah sintaks yang menyoroti satu-satunya tujuan? Jika demikian, beberapa aturan kunci font cerdas mungkin jauh lebih mudah daripada mengacaukan tabel sintaksis.
Malabarba
@ Malabarba: sebagian besar meskipun akan lebih baik jika perintah gerakan bekerja seperti yang diharapkan dalam bit lispy. Saya telah mencoba mengacaukan font-lock tetapi tidak bisa membuatnya berfungsi dengan baik: git.linaro.org/people/alex.bennee/ lava-mode.git / blob / HEAD: /…
stsquad

Jawaban:

7

Anda tidak ingin membuat sarang satu tabel sintaks (yang merupakan struktur vektor) di dalam yang lain, Anda ingin mengatur buffer di mana, tergantung pada posisinya, satu tabel sintaks akan digunakan sebagai pengganti yang lain.

Jawaban lain menjelaskan bagaimana melakukan ini menggunakan syntax-tableproperti teks. Berikut ini cara melakukannya menggunakan salah satu paket "mode utama ganda", mmm-mode . Ini akan menggunakan segalanya mode primer di tingkat atas buffer, dan tabel sintaks submode, aturan font-lock, keymap, dll di "subregion".

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

Ini mengasumsikan bahwa file mode campuran Anda dinamai *.el.json. Sesuaikan sesuai kebutuhan.

Sekarang, instal mmm-mode, evaluasi hal di atas dan (hanya kemudian) buka salah satu file yang dimaksud.

Dmitry
sumber
Saya kira saya belum sepenuhnya membungkus kepala saya di sekitar bagaimana tabel sintaks bekerja. Saya berasumsi mereka bersarang karena harus ada keadaan tergantung pada karakter sebelumnya? Pengalaman terakhir saya dari mmm-mode adalah nxhtml-mode tapi saya merasa agak kikuk, itulah sebabnya saya beralih menggunakan mode web.
stsquad
nxhtml-modeagak kikuk, ya. mmm-modekurang begitu, tetapi masih kompleks. Saya tidak yakin apakah Anda dapat melakukan hal seperti ini di web-mode; tanya penulisnya, mungkin.
Dmitry
Tabel sintaks tidak mengandung status apa pun. Terkait, syntax-ppsscache tidak.
Dmitry
maaf saya belum menerima jawabannya. Saya belum sempat melihat mmm-mode. Saya tidak yakin apakah ini berarti saya harus mengucapkan kembali pertanyaan atau membiarkannya terus menggantung?
stsquad
Saya tidak keberatan. Tetapi jika Anda ingin saya menyajikan jawaban yang lebih lengkap menggunakan mmm-mode, Anda harus menyertakan cuplikan contoh markup ganda Anda dalam pertanyaan.
Dmitry
7

Ok, mari kita luruskan beberapa dasar.

Tabel sintaks Nesting dimungkinkan

Tabel sintaks tidak harus bersifat global ke seluruh buffer. Anda dapat menerapkannya sebagai properti teks ke wilayah tertentu. Ini berarti Anda memang dapat menerapkan elisptabel sintaks hanya untuk wilayah yang dikelilingi oleh backtick.

Bagaimana kamu melakukannya?

Inilah salah satu cara Anda dapat melakukannya. Metode ini melakukannya segera sebelum font-lock berjalan melalui buffer, jadi metode ini harus secara khusus mencegah masalah penguncian font Anda.

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

Dalam definisi mode-utama Anda, Anda harus menambahkan:

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

Tabel sintaksis tidak sama dengan penyorotan sintaksis

Highlighter sintaks (sistem font-lock) menggunakan Tabel Sintaks sebagai bagian dari informasinya, sehingga solusi di atas harus mencegah stabilo agar tidak menjadi gila.

Namun, itu hanya bagian dari data, jika Anda juga ingin teks di backticks berwarna persis seperti yang Anda lihat di buffer elisp, Anda harus memperluas fungsi di atas untuk melakukannya secara khusus.

(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
sumber
dapatkah kamu sedikit lebih spesifik? Apakah saya perlu membuat panggilan ke font untuk melakukan pewarnaan elisp?
stsquad
@stsquad Saya telah memperluas jawaban dengan contoh di mana Anda dapat memulai untuk itu. Saya akan memberikan solusi yang lengkap, tetapi saya mengalami satu kendala setelah yang lainnya, dan akhirnya mencapai kesimpulan bahwa ini akan menjadi pertanyaan yang sama sekali berbeda.
Malabarba
@ Malabarba Terima kasih atas informasinya, tetapi solusi lengkap Anda terlihat salah arah: properti teks adalah bagian dari teks buffer, jadi Anda membuat buffer dimodifikasi setiap kali di-fontified. Itu akan membatalkan beberapa cache dan membuat Emacs mengkonsumsi lebih banyak CPU tanpa alasan yang jelas. Anda setidaknya harus menggunakan with-silent-modifications(seperti font-lock-default-fontify-regionhalnya), tetapi yang lebih baik adalah memindahkan add-text-propertiesbisnis ini syntax-propertize-function.
Dmitry