Previous: , Up: コードの自動インデント   [Contents][Index]


24.7.2 パーサーベースのインデント

tree-sitterライブラリー(プログラムソースの解析を参照)とともにEmacsをビルドした場合には、Emacsによるプログラムソースのパースと構文ツリー(syntax tree)の生成が可能になります。プログラムソースのインデントコマンドにたいするガイド役としてこの構文ツリーを使用することができます。柔軟性を最大限発揮できるように構文ツリーに問い合わせを行うインデント用のカスタマイズ関数を記述して、それぞれの言語に応じたインデントを行うことも可能ですが、これには多くの作業が伴います。より使いやすいのは、この後に説明するシンプルなインデント用エンジンでしょう。そうすればメジャーモードに要求されるのはいくつかのインデントルールの記述だけとなり、残りはこのエンジンが面倒を見てくれます。

パーサーベースのインデントエンジンを有効にするにはtreesit-simple-indent-rulesをセットしてtreesit-major-mode-setupを呼び出すか、indent-line-functionの値をtreesit-indentにセットしてください(どちらを選んでも同じ)。

Variable: treesit-indent-function

この変数はtreesit-indentによって呼び出される実際の関数が格納される。デフォルトの値はtreesit-simple-indent。将来より複雑なエンジンが追加されるかもしれない。

インデントルールの記述

Variable: treesit-simple-indent-rules

このローカル変数にはすべての言語用のインデントルールが格納される。値は(language . rules)というフォームのalist。ここでlanguageは言語シンボル、rules(matcher anchor offset)という形式の要素をもつリスト。

最初にEmacsはカレント行の先頭にある最小のtree-sitterノードをmatcherに渡して、非nilがリターンされればそのルールが適用できる。その後にEmacsはそのノードをanchorに渡して、バッファーの位置がリターンされる。Emacsがその位置の列番号を取得してoffsetを追加すると、、その結果がカレント行のインデント列となる。

matcheranchorは関数。これらにたいしてEmacsは便利なデフォルトを提供している。

matcherおよびanchorはそれぞれnodeparentbolという3つの引数を受け取る関数。引数bolはインデントが要求されるバッファー位置(行頭の後の最初の非空白文字の位置)、引数nodeはその位置から開始されるもっとも大きい(かつルートではない)ノード、parentnodeの親ノードである。ただしその位置にあるのが空白だったり、あるいは複数行文字列の内部の場合には、その位置から開始可能なノードは存在しないので、nodenilとなる。このような場合には、その位置を跨ぐもっとも小さいノードがparentとなる。

ルールが適用可能ならmatcherは非nilanchorはバッファー位置をリターンすること。

offsetは整数、値が整数であるような変数、あるいは整数をリターンする関数を指定できる。関数の場合にはmatcherやanchorと同様にnodeparentbolが渡される。

Variable: treesit-simple-indent-presets

これはtreesit-simple-indent-rulesmatcheranchorにたいするデフォルトのリスト。これらはそれぞれnodeparentbolという3つの引数を受け取る関数である。利用できるデフォルト関数は以下のとおり:

no-node

このmatcherはnodeparentbolという3つの引数で呼び出される。nodenil (bolで始まるノードが存在せず、bolが空行や複数行の内部にbolがある場合などが該当)の場合には、マッチを表す非nilをリターンする。

parent-is

このmatcherはtypeという1つの引数で呼び出される関数をリターンする。この関数はnodeparentbolという3つの引数とともに呼び出されて、parentのタイプがregexpのtypeとマッチすれば非nil (マッチしたことを意味する)をリターンする。

node-is

このmatcherはtypeという1つの引数を受け取る関数。この関数はnodeparentbolという3つの引数とともに呼び出されて、nodeのタイプがregexpのtypeとマッチすれば非nilをリターンする。

field-is

このmatcherはnameという1つの引数を受け取る関数。この関数はnodeparentbolという3つの引数とともに呼び出されて、parentnodeのフィールド名がregexpのnameとマッチすれば非nilをリターンする。

query

このmatcherはqueryという1つの引数を受け取る関数。この関数はnodeparentbolという3つの引数とともに呼び出されて、queryparentに問い合わせた場合にnodeをキャプチャすれば非nilをリターンする(tree-sitterノードにたいするパターンマッチングを参照)。

match

このmatcherはnode-typeparent-typenode-fieldnode-index-minnode-index-maxという5つの引数とともに呼び出される関数。ここでリターンされた関数はnodeparentbolという3つの引数で呼び出される関数をリターンする。ここでリターンされた関数はnodeのタイプがregexpのnode-typeparentのタイプがregexpのparent-typeparentnodeのフィールド名がregexpのnode-fieldnodeのインデックスが兄弟ノードnode-index-minnode-index-maxの間にあれば非nilをリターンする。このmatcherは引数の値がnilであれば、その引数はチェックしない。たとえば親ノードとしてargument_listをもつ最初の子ノードにマッチさせるには、以下のようにすればよい

(match nil "argument_list" nil 0 0)

更にnode-typeは特別な値nullでもよく、これはnodeの値がnilのときにマッチする。

n-p-gp

“node-parent-grandparent(ノード-親-祖父母)”の略。このmatcherはnode-typeparent-typegrandparent-typeという3つの引数を受け取る関数。これはnodeparentbolという3つの引数で呼び出されて(1) node-typenode’のタイプとマッチ、(2) parent-typeparentのタイプとマッチ (3) grandparent-typeparentの親のタイプという3つのマッチがすべて成り立てば非nilをリターンする関数をリターンする。この関数はnode-typeparent-typegrandparent-typeのいずれかがnilならチェックを行わない。

comment-end

このmatcherはnodeparentbolという3つの引数で呼び出されて、コメント終了トークンn前にポイントがあれば非nilをリターンする関数。コメント終了トークンはcomment-end-skipのregexpによって定義される。

catch-all

このmatcherはnodeparentbolという3つの引数で呼び出される関数。この関数はマッチを示すために常に非nilをリターンする。

first-sibling

このanchorはnodeparentbolという3つの引数とともに呼び出されて、parentの最初の子ノードの開始をリターンする関数。

nth-sibling

このanchorはn、オプションとしてnamedという2つの引数を受け取る関数。nodeparentbolという3つn引数で呼び出されて、parentn番目の開始をリターンする関数をリターンする。namedが非nilなら、名前付きの子だけを考慮する(named nodeを参照)。

parent

このanchorはnodeparentbolという3つの引数とともに呼び出されて、parentの開始をリターンする関数。

grand-parent

このanchorはnodeparentbolという3つの引数とともに呼び出されて、parentの親の開始をリターンする関数。

great-grand-parent

このanchorはnodeparentbolという3つの引数とともに呼び出されて、parentの親の親の開始をリターンする関数。

parent-bol

このanchorはnodeparentbolという3つの引数とともに呼び出されて、parentの開始位置にある行の最初の非スペース文字をリターンする関数。

standalone-parent

このanchorはnodeparentbolという3つの引数で呼び出される関数。この関数は独自の行で始まるようなnodeの最初の祖先(親、祖父母、etc)を探して、そのノードの開始をリターンする。“独自の行で始まる”とはそのノードが開始する行において、ノードの前に空白文字しか存在しないことを意味する。

prev-sibling

このanchorはnodeparentbolという3つの引数とともに呼び出されて、nodeの前の兄弟ノードの開始をリターンする関数。

no-indent

このanchorはnodeparentbolという3つの引数とともに呼び出されて、nodeの開始をリターンする関数。

prev-line

このanchorはnodeparentbolという3つの引数とともに呼び出されて、前の行の最初の必要空白文字をリターンする関数。

column-0

このanchorはnodeparentbolという3つの引数とともに呼び出されて、列0にあるカレント行先端をリターンする。

comment-start

このanchorはnodeparentbolという3つの引数とともに呼び出されて、コメント開始トークンの後の位置をリターンする。コメント開始トークンは正規表現comment-start-skipによって定義される。この関数はparentがコメントノードであるとみなす。

prev-adaptive-prefix

このanchorはnodeparentbolという3つの引数で呼び出される関数。前にある空ではない行の先頭にあるテキストにたいして、adaptive-fill-regexpとのマッチを試みる。マッチが存在すればマッチの終端、存在しなければnilをリターンする。ただしカレント行がプレフィックス(例: ‘-’)で始まる場合には、前の行のプレフィックスと位置が揃うように、前の行のプレフィックスの開始をリターンする。このanchorはブロックコメントにたいして、indent-relative-*のような挙動のインデントを行う際に役に立つ。

インデント用のユーティリティー

以下にパーサーベースのインデントルールを記述する助けとなるユーティリティー関数をいくつか挙げます。

Command: treesit-check-indent mode

このコマンドはメジャーモードmodeにたいするカレントバッファーのインデントをチェックする。この関数はmodeに応じてカレントバッファーをインデントして、その結果をカレントのインデントと比較、その後に差分を表示するバッファーをポップアップする。(インデント対象の)正しいインデントはグリーン、カレントのインデントは赤のカラーで示される。

インデントのルールを記述する際には、treesit-inspect-modeを使用するのも助けとなるでしょう(tree-sitterの言語グラマーを参照)。


Previous: SMIE: 無邪気なインデントエンジン, Up: コードの自動インデント   [Contents][Index]