Làm thế nào tôi có thể lồng một bảng cú pháp trong một bảng khác?


8

Tôi đã viết một chế độ đơn giản để xử lý JSON. Nó sử dụng các máy móc có nguồn gốc để sử dụng lại hầu hết mã của chế độ json. Tuy nhiên, một bổ sung là bạn có thể chèn elisp vào văn bản JSON được đánh giá tại thời điểm gửi JSON. Ví dụ, một đoạn trích của json trông như thế này:

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

Hiện tại cú pháp tô sáng cú pháp của văn bản này đã bị phá vỡ khi cú pháp JSON hightllight bị ném bởi elisp. Tôi muốn thiết lập một bảng cú pháp lồng nhau để elisp được nhận dạng đúng là elisp khi bên trong các ký tự thoát (tôi đã chọn `trong trường hợp này). Tôi hiểu rằng bạn có thể tham gia các bảng char (bảng cú pháp được xây dựng từ đó) với một cái gì đó như:

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

Nhưng tôi không hiểu làm thế nào tôi có thể sửa đổi bảng cú pháp để bắt đầu sử dụng bảng cú pháp con (elisp) trong khi giữa các ký tự thoát?


1
Là cú pháp làm nổi bật mục tiêu duy nhất? Nếu vậy, một số quy tắc khóa phông chữ thông minh có thể dễ dàng hơn nhiều so với việc làm rối với các bảng cú pháp.
Malabarba

@Malabarba: chủ yếu mặc dù sẽ rất tuyệt nếu các lệnh chuyển động hoạt động như mong đợi trong các bit lispy. Tôi đã thử làm rối với khóa phông chữ nhưng không thể làm cho nó hoạt động chính xác: git.linaro.org/people/alex.bennee/ dung nham-mode.git / blob / HEAD: / ...
stsquad

Câu trả lời:


7

Bạn không muốn lồng một bảng cú pháp (là cấu trúc vectơ) bên trong một bảng khác, bạn muốn thiết lập một bộ đệm trong đó, tùy thuộc vào vị trí, một bảng cú pháp sẽ được sử dụng thay cho bảng khác.

Câu trả lời khác mô tả cách thực hiện việc này bằng cách sử dụng thuộc tính syntax-tablevăn bản. Đây là cách thực hiện bằng một trong các gói "nhiều chế độ chính", chế độ mmm . Nó sẽ sử dụng mọi thứ của chế độ chính ở cấp cao nhất của bộ đệm và bảng cú pháp của mã con, quy tắc khóa phông chữ, sơ đồ bàn phím, v.v. trong "tiểu vùng".

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

Điều này giả định rằng các tập tin chế độ hỗn hợp của bạn được đặt tên *.el.json. Điều chỉnh cho phù hợp.

Bây giờ, cài đặt mmm-mode, đánh giá ở trên và (chỉ sau đó) mở một trong các tệp được đề cập.


Tôi đoán rằng tôi đã không hoàn toàn quấn đầu xung quanh cách các bảng cú pháp hoạt động. Tôi giả sử họ lồng nhau vì phải có trạng thái phụ thuộc vào các ký tự trước? Trải nghiệm cuối cùng của tôi về chế độ mmm là chế độ nxhtml nhưng tôi thấy nó khá cồng kềnh, đó là lý do tại sao tôi chuyển sang sử dụng chế độ web.
stsquad

nxhtml-modelà khá lộn xộn, vâng. mmm-modeít hơn, nhưng nó vẫn phức tạp. Tôi không chắc nếu bạn có thể làm một cái gì đó như thế này trong web-mode; hỏi tác giả của nó, có thể.
Dmitry

Các bảng cú pháp không chứa bất kỳ trạng thái nào. Liên quan, syntax-ppssbộ nhớ cache nào.
Dmitry

xin lỗi tôi chưa chấp nhận câu trả lời Tôi không có thời gian để xem chế độ mmm. Tôi không chắc điều này có nghĩa là tôi nên viết lại câu hỏi hay để nó tiếp tục treo?
stsquad

Tôi không phiền Nhưng nếu bạn muốn tôi trình bày câu trả lời đầy đủ hơn bằng chế độ mmm, bạn nên đưa đoạn trích mẫu của đánh dấu kép của bạn vào câu hỏi.
Dmitry

7

Ok, chúng ta hãy lấy một số điều cơ bản thẳng.

Bảng cú pháp lồng nhau là có thể

Các bảng cú pháp không phải là toàn cục cho toàn bộ bộ đệm. Bạn có thể áp dụng chúng làm thuộc tính văn bản cho các vùng cụ thể. Điều này có nghĩa là bạn thực sự chỉ có thể áp dụng elispbảng cú pháp cho các vùng được bao quanh bởi backticks.

Làm thế nào để bạn làm điều đó?

Đây là một cách bạn có thể làm điều đó. Phương pháp này thực hiện ngay lập tức trước khi khóa phông chữ chạy qua bộ đệm, do đó, nó sẽ đặc biệt ngăn chặn các sự cố khóa phông chữ của bạn.

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

Trong định nghĩa chế độ chính của bạn, bạn sẽ cần thêm:

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

Các bảng cú pháp không giống như tô sáng cú pháp

Cú pháp tô sáng (hệ thống khóa phông chữ) sử dụng Bảng cú pháp như một phần thông tin của nó, vì vậy giải pháp ở trên sẽ ngăn không cho công cụ tô sáng đi.

Tuy nhiên, đó chỉ là một phần của dữ liệu, nếu bạn cũng muốn văn bản trong backticks được tô màu chính xác như bạn thấy trong bộ đệm elisp, bạn sẽ phải mở rộng chức năng ở trên để thực hiện điều đó một cách cụ thể.

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

bạn có thể chi tiết hơn một chút không? Tôi có cần lồng một cuộc gọi đến khóa phông chữ để thực hiện tô màu không?
stsquad

@stsquad Tôi đã mở rộng câu trả lời bằng một ví dụ về nơi bạn có thể bắt đầu cho điều đó. Tôi sẽ cung cấp một giải pháp hoàn chỉnh, nhưng tôi gặp một trở ngại khác, và cuối cùng đã đi đến kết luận rằng đây sẽ là một câu hỏi hoàn toàn khác.
Malabarba

@Malabarba Cảm ơn thông tin, nhưng giải pháp đầy đủ của bạn có vẻ sai lầm: thuộc tính văn bản là một phần của văn bản bộ đệm, vì vậy bạn đang làm cho bộ đệm được sửa đổi mỗi khi nó được phông chữ. Điều đó sẽ làm mất hiệu lực một số bộ nhớ cache và khiến Emacs tiêu tốn nhiều CPU hơn mà không có lý do chính đáng. Ít nhất bạn nên sử dụng with-silent-modifications(như thế font-lock-default-fontify-region), nhưng thậm chí tốt hơn là chuyển add-text-propertiesdoanh nghiệp này sang syntax-propertize-function.
Dmitry
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.