tree-sitterライブラリー(プログラムソースの解析を参照)とともにEmacsをビルドした場合には、Emacsによるプログラムソースのパースと構文ツリー(syntax tree)の生成が可能になります。プログラムソースのインデントコマンドにたいするガイド役としてこの構文ツリーを使用することができます。柔軟性を最大限発揮できるように構文ツリーに問い合わせを行うインデント用のカスタマイズ関数を記述して、それぞれの言語に応じたインデントを行うことも可能ですが、これには多くの作業が伴います。より使いやすいのは、この後に説明するシンプルなインデント用エンジンでしょう。そうすればメジャーモードに要求されるのはいくつかのインデントルールの記述だけとなり、残りはこのエンジンが面倒を見てくれます。
パーサーベースのインデントエンジンを有効にするには、treesit-simple-indent-rules
かtreesit-simple-indent-rules
のいずれかをセットしてからtreesit-major-mode-setup
を呼び出してください(treesit-major-mode-setup
が行うのはindent-line-function
の値をtreesit-indent
、indent-region-function
の値をtreesit-indent-region
にセットすることがすべて)。
この変数はtreesit-indent
によって呼び出される実際の関数が格納される。デフォルトの値はtreesit-simple-indent
。将来より複雑なエンジンが追加されるかもしれない。
このローカル変数にはすべての言語用のインデントルールが格納される。値は(language . rules)
というフォームのalist。ここでlanguageは言語シンボル、rulesは(matcher anchor offset)
という形式の要素をもつリスト。
最初にEmacsはカレント行の先頭にある最小のtree-sitterノードをmatcherに渡して、非nil
がリターンされればそのルールが適用できる。その後にEmacsはそのノードをanchorに渡して、バッファーの位置がリターンされる。Emacsがその位置の列番号を取得してoffsetを追加すると、、その結果がカレント行のインデント列となる。
matcherとanchorは関数。これらにたいしてEmacsは便利なデフォルトを提供している。
matcherおよびanchorはそれぞれnode、parent、bolという3つの引数を受け取る関数。引数bolはインデントが要求されるバッファー位置(行頭の後の最初の非空白文字の位置)、引数nodeはその位置から開始されるもっとも大きい(かつルートではない)ノード、parentはnodeの親ノードである。ただしその位置にあるのが空白だったり、あるいは複数行文字列の内部の場合には、その位置から開始可能なノードは存在しないので、nodeはnil
となる。このような場合には、その位置を跨ぐもっとも小さいノードがparentとなる。
ルールが適用可能ならmatcherは非nil
、anchorはバッファー位置をリターンすること。
offsetは整数、値が整数であるような変数、あるいは整数をリターンする関数を指定できる。関数の場合にはmatcherやanchorと同様にnode、parent、bolが渡される。
これはtreesit-simple-indent-rules
のmatcherとanchorにたいするデフォルトのリスト。これらはそれぞれnode、parent、bolという3つの引数を受け取る関数である。利用できるデフォルト関数は以下のとおり:
no-node
¶このmatcherはnode、parent、bolという3つの引数で呼び出される。nodeがnil
(bolで始まるノードが存在せず、bolが空行や複数行の内部にbolがある場合などが該当)の場合には、マッチを表す非nil
をリターンする。
parent-is
¶このmatcherはtypeという1つの引数で呼び出される関数をリターンする。この関数はnode、parent、bolという3つの引数とともに呼び出されて、parentのタイプがregexpのtypeとマッチすれば非nil
(マッチしたことを意味する)をリターンする。
node-is
¶このmatcherはtypeという1つの引数を受け取る関数。この関数はnode、parent、bolという3つの引数とともに呼び出されて、nodeのタイプがregexpのtypeとマッチすれば非nil
をリターンする。
field-is
¶このmatcherはnameという1つの引数を受け取る関数。この関数はnode、parent、bolという3つの引数とともに呼び出されて、parentのnodeのフィールド名がregexpのnameとマッチすれば非nil
をリターンする。
query
¶このmatcherはqueryという1つの引数を受け取る関数。この関数はnode、parent、bolという3つの引数とともに呼び出されて、queryでparentに問い合わせた場合にnodeをキャプチャすれば非nil
をリターンする(tree-sitterノードにたいするパターンマッチングを参照)。
match
¶このmatcherはnode-type、parent-type、node-field、node-index-min、node-index-maxという5つの引数とともに呼び出される関数。ここでリターンされた関数はnode、parent、bolという3つの引数で呼び出される関数をリターンする。ここでリターンされた関数はnodeのタイプがregexpのnode-type、parentのタイプがregexpのparent-type、parentのnodeのフィールド名がregexpのnode-field、nodeのインデックスが兄弟ノードnode-index-minとnode-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-type、parent-type、grandparent-typeという3つの引数を受け取る関数。これはnode、parent、bolという3つの引数で呼び出されて(1)
node-typeがnode’のタイプとマッチ、(2)
parent-typeがparentのタイプとマッチ (3)
grandparent-typeがparentの親のタイプという3つのマッチがすべて成り立てば非nil
をリターンする関数をリターンする。この関数はnode-type、parent-type、grandparent-typeのいずれかがnil
ならチェックを行わない。
comment-end
¶このmatcherはnode、parent、bolという3つの引数で呼び出されて、コメント終了トークンn前にポイントがあれば非nil
をリターンする関数。コメント終了トークンはcomment-end-skip
のregexpによって定義される。
catch-all
¶このmatcherはnode、parent、bolという3つの引数で呼び出される関数。この関数はマッチを示すために常に非nil
をリターンする。
first-sibling
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、parentの最初の子ノードの開始をリターンする関数。
nth-sibling
¶このanchorはn、オプションとしてnamedという2つの引数を受け取る関数。node、parent、bolという3つn引数で呼び出されて、parentのn番目の開始をリターンする関数をリターンする。namedが非nil
なら、名前付きの子だけを考慮する(named nodeを参照)。
parent
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、parentの開始をリターンする関数。
grand-parent
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、parentの親の開始をリターンする関数。
great-grand-parent
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、parentの親の親の開始をリターンする関数。
parent-bol
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、parentの開始位置にある行の最初の非スペース文字をリターンする関数。
standalone-parent
¶このanchorはnode、parent、bolという3つの引数で呼び出される関数。この関数は独自の行で始まるようなnodeの最初の祖先(親、祖父母、etc)を探して、そのノードの開始をリターンする。“独自の行で始まる”とはそのノードが開始する行において、ノードの前に空白文字しか存在しないことを意味する。
prev-sibling
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、nodeの前の兄弟ノードの開始をリターンする関数。
no-indent
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、nodeの開始をリターンする関数。
prev-line
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、前の行の最初の必要空白文字をリターンする関数。
column-0
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、列0にあるカレント行先端をリターンする。
comment-start
¶このanchorはnode、parent、bolという3つの引数とともに呼び出されて、コメント開始トークンの後の位置をリターンする。コメント開始トークンは正規表現comment-start-skip
によって定義される。この関数はparentがコメントノードであるとみなす。
prev-adaptive-prefix
¶このanchorはnode、parent、bolという3つの引数で呼び出される関数。前にある空ではない行の先頭にあるテキストにたいして、adaptive-fill-regexp
とのマッチを試みる。マッチが存在すればマッチの終端、存在しなければnil
をリターンする。ただしカレント行がプレフィックス(例:
‘-’)で始まる場合には、前の行のプレフィックスと位置が揃うように、前の行のプレフィックスの開始をリターンする。このanchorはブロックコメントにたいして、indent-relative
-*のような挙動のインデントを行う際に役に立つ。
以下にパーサーベースのインデントルールを記述する助けとなるユーティリティー関数をいくつか挙げます。
このコマンドはメジャーモードmodeにたいするカレントバッファーのインデントをチェックする。この関数はmodeに応じてカレントバッファーをインデントして、その結果をカレントのインデントと比較、その後に差分を表示するバッファーをポップアップする。(インデント対象の)正しいインデントはグリーン、カレントのインデントは赤のカラーで示される。
インデントのルールを記述する際には、treesit-inspect-mode
を使用するのも助けとなるでしょう(Tree-sitter言語グラマーを参照)。