構文テーブルを別の構文テーブルにネストするにはどうすればよいですか?


8

JSONを処理するためのシンプルなモードを作成しました。派生した機構を使用して、json-modeのコードのほとんどを再利用します。ただし、1つの追加として、JSON送信時に評価されるJSONテキストにelispを挿入できます。たとえば、jsonの抜粋は次のようになります。

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

現在、このテキストの構文の強調表示は、elispによってJSON構文のハイライトがスローされるため、壊れています。ネストされた構文テーブルをセットアップして、エスケープ文字内でelispがelispとして正しく認識されるようにしたい(この場合は `を選択した)。char-tables(構文テーブルの作成元)を次のように結合できることを理解しています。

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

しかし、エスケープ文字の間で子(elisp)構文テーブルの使用を開始するように構文テーブルを変更する方法がわかりません。


1
構文の強調表示が唯一の目的ですか?もしそうなら、いくつかのスマートなフォントロックルールは、構文テーブルをいじるよりもはるかに簡単かもしれません。
マラバルバ2014年

@Malabarba:ほとんどの場合、移動コマンドがlispyビットで期待どおりに機能するのは良いことですが、私はfont-lockをいじってみましたが、適切に機能させることができませんでした:git.linaro.org/people/alex.bennee/ lava-mode.git / blob / HEAD:/…
stsquad 2014年

回答:


7

あるシンタックステーブル(ベクター構造)を別のシンタックステーブル内にネストするのではなく、位置に応じて1つのシンタックステーブルを他のシンタックステーブルの代わりに使用するバッファーを設定します。

もう1つの答えは、syntax-tabletextプロパティを使用してこれを行う方法を説明しています。これは、「マルチメジャーモード」パッケージの1つであるmmm-modeを使用して行う方法です。バッファのトップレベルにあるプライマリモードのすべて、および「サブ領域」のサブモードの構文テーブル、フォントロックルール、キーマップなどを使用します。

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

これは、混合モードファイルの名前がであることを前提としています*.el.json。必要に応じて調整します。

次に、をインストールしmmm-mode、上記を評価して、(そのときだけ)問題のファイルの1つを開きます。


構文テーブルがどのように機能するかについて、完全に頭を抱えていないと思います。以前のキャラクターに依存状態があるはずなので、それらはネストされていると思いましたか?mmm-modeの最後の経験はnxhtml-modeでしたが、かなり不格好であることがわかったため、webモードの使用に切り替えました。
stsquad 2014年

nxhtml-modeはい、かなり不格好です。mmm-modeそれほどではありませんが、それでもまだ複雑です。でこのようなことができるかどうかはわかりませんweb-mode。おそらくその作者に尋ねてください。
ドミトリー

構文テーブルには状態が含まれていません。関連して、syntax-ppssキャッシュはそうします。
ドミトリー

すみません、まだ答えを受け入れていません。mmmモードを見る時間がありませんでした。これが質問を書き直す必要があるのか​​、それともハングアップさせ続けるのかはわかりませんか?
stsquad 2014年

私は気にしません。しかし、mmm-modeを使用してより完全な回答を提示したい場合は、二重マークアップのサンプルスニペットを質問に含める必要があります。
ドミトリー

7

さて、いくつかの基本をまっすぐにしましょう。

構文テーブルのネストが可能です

構文テーブルは、バッファ全体に対してグローバルである必要はありません。それらをテキストプロパティとして特定の領域に適用できます。これは、バッククォートで囲まれた領域にのみ構文テーブルを適用できることを意味しますelisp

どうやってやるの?

これを行う方法の1つを次に示します。このメソッドは、フォントロックがバッファを通過する直前にそれを実行するため、フォントロックの問題を特に防止する必要があります。

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

メジャーモードの定義では、以下を追加する必要があります。

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

構文テーブルは構文強調表示と同じではありません

シンタックスハイライター(フォントロックシステム)は、情報の一部としてシンタックステーブルを使用するため、上記のソリューションでは、ハイライターが正常に機能しないようにする必要があります。

ただし、これはデータの一部にすぎません。elispバッファーで表示されるように、バッククォート内のテキストも正確に色付けしたい場合は、上記の関数を拡張して具体的に行う必要があります。

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

あなたはもう少し具体的にすることができますか?elispの色付けを行うには、フォントロックの呼び出しをネストする必要がありますか?
stsquad 2014年

@stsquad私はあなたがそれを始めることができる場所の例で答えを拡張しました。私は完全な解決策を提供するつもりでしたが、次々に障害に遭遇し、結局これはまったく別の問題であるという結論に達しました。
マラバルバ2014年

@Malabarba情報ありがとうございます。ただし、完全なソリューションは見当違いに見えます。テキストプロパティはバッファテキストの一部であるため、フォントがフォント化されるたびにバッファを変更しています。それはいくつかのキャッシュを無効にし、Emacsが正当な理由なくより多くのCPUを消費するようにします。少なくとも使用する必要がありますがwith-silent-modifications(使用するようにfont-lock-default-fontify-region)、このadd-text-propertiesビジネスをに移動することをお勧めしsyntax-propertize-functionます。
ドミトリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.