GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Using-Parser.html ・ emacs-doc.conao3.com

38.2 Tree-sitterパーサーの使用

本節では、Tree-sitterパーサーの作成と設定方法について説明します。Emacsでは、各Tree-sitterパーサーがバッファに関連付けられています。ユーザーがバッファを編集すると、関連付けられたパーサーと構文木が自動的に最新の状態に保たれます。

変数: treesit-max-buffer-size

この変数には、Tree-sitterを有効にできるバッファの最大サイズが格納されています。メジャーモードは、Tree-sitterの機能を有効にするかどうかを判断する際に、この値を確認する必要があります。

変数: treesit-languages-require-line-column-tracking

Emacsはデフォルトでは、バッファ内の位置に対する行番号や列番号を追跡しません。しかし、一部の言語の文法では、構文解析に行番号や列番号の情報を利用します。これらの言語のパーサーをバッファ内で作成した場合、Emacsは行番号と列番号の追跡を有効にし、これらの情報をパーサーに報告します。一度バッファが行番号と列番号の追跡を開始すると、それ以降は追跡を停止しません。また、パーサーが行番号・列番号を追跡する/しない設定で作成されると、この変数の変更に関わらず、その設定が維持されます。

この変数は、行・列番号の追跡を必要とする言語のリストです。大多数の言語では、行・列番号の情報は必要ありません。これまでのところ、それを必要とすることが分かっているのはHaskellのみです。

ユーザーは treesit-tracking-line-column-p と treesit-parser-tracking-line-column-p を使用して、バッファまたはパーサーがそれぞれ行と列を追跡しているかを確認できます。

関数: treesit-parser-create language &optional buffer no-reuse tag

指定された buffer および language(Tree-sitter Language Grammar を参照)と、tag 用のパーサーを作成します。buffer が省略されているか、あるいは nil である場合は、現在のバッファを指します。

デフォルトでは、この関数は buffer 内の tag に対して language 用のパーサーが既に存在する場合はそれを再利用しますが、no-reuse が nil 以外の場合は、常に新しいパーサーを作成します。

tag は t 以外の任意の記号を指定でき、デフォルトは nil です。異なるパーサーで同じタグが使われる場合があります。

パーサーが指定されている場合、その情報をクエリできます。treesit-parser-buffer はパーサーに関連付けられたバッファを、treesit-parser-language はパーサーで使用されている言語を返します。パーサーのタグは treesit-parser-tag で取得可能です。また、各パーサーはオプションで埋め込みレベルを保持しており、これは treesit-parser-set-embed-level で設定し、treesit-parser-embed-level で取得できます(埋め込みレベルの詳細については、parser-embed-level を参照してください)。最後に、treesit-parser-p はオブジェクトが Tree-sitter パーサーであるかどうかを示します。

バッファを明示的にパースする必要はありません。パースは自動的に、かつ遅延的に実行されるためです。パーサーがパースを行うのは、Lispプログラムが構文木内のノードを問い合わせたときのみです。したがって、パーサーが最初に生成された時点ではバッファをパースせず、Lispプログラムが初めてノードを問い合わせるまで待機します。同様に、バッファに変更が加えられた場合でも、パーサーは直ちに再パースすることはありません。

パーサーが解析を行う際は、バッファのサイズを確認します。Tree-sitterで扱えるバッファは、約4GBまでです。サイズがこれを超える場合、Emacsはバッファサイズをシグナルデータとしてtreesit-buffer-too-largeエラーを発生させます。

パーサーが作成されると、Emacsは自動的にそれを内部パーサーリストに追加します。バッファに変更が加わるたびに、Emacsはこのリスト内のパーサーを更新し、構文木を漸進的に更新できるようにします。

関数: treesit-parser-list &optional buffer language tag

この関数は、language と tag でフィルタリングされた buffer のパーサーリストを返します。buffer が nil であるか、または省略されている場合は、現在のバッファがデフォルトとなります。

language が nil 以外の場合は、その言語のパーサーのみを含め、tag を持つパーサーのみを含めてください。tag のデフォルト値は nil です。tag が t の場合は、タグに関わらずパーサーを返されるリストに含めてください。

関数: treesit-parser-delete parser

この関数は parser を削除します。

通常、パーサーはバッファ全体を「認識」しますが、バッファが狭められた場合(狭めを参照)、パーサーはバッファのアクセス可能な部分のみを認識します。パーサーから見れば、非表示領域は削除されたことになります。その後、バッファが広げられると、パーサーは先頭と末尾にテキストが挿入されたと判断します。パーサーは狭めの処理に対応していますが、モード側で狭めを多言語バッファの処理手段として使用すべきではありません。代わりに、パーサーが動作すべき範囲を設定してください。多言語テキストの解析を参照してください。

パーサーは遅延的に解析を行うため、ユーザーやLispプログラムがバッファを狭めても、パーサーに即座に影響が及ぶことはありません。バッファが狭められた状態でモードがノードを照会しない限り、パーサーはその狭小化を認識しません。

バッファ用のパーサーを作成するほか、Lispプログラムは文字列をパースすることもできます。バッファとは異なり、文字列のパースは一度限りの操作であり、結果を更新する方法はありません。

関数: treesit-parse-string string language

この関数は language を用いて string を解析し、生成された構文木のルートノードを返します。この関数をループ内で使用しないでください。本関数は単発利用を想定した便宜上の関数であり、最適化されていません。負荷の高い処理を行う場合は、代わりに一時バッファを使用してください。

構文木の変更を通知する

Lispプログラムでは、インクリメンタルパースの影響を受けたテキストについて通知を受け取りたい場合があります。例えば、コメント終了トークンを挿入すると、そのトークンより前のテキストがコメントに変換されます。テキストが直接編集されたわけではありませんが、それでも「変更された」とみなされます。

Emacsでは、Lispプログラムがこうした変更に対するコールバック関数(別名 notifier)を登録できます。notifier関数は、ranges と parser の2つの引数を取ります。ranges は (start . end) 形式のコンスセルのリストであり、ここで start と end は範囲の開始位置と終了位置を示します。parser は通知を発行するパーサーです。

パーサーがバッファを再解析するたびに、新旧の構文木を比較し、ノードが変更された範囲を算出して、その範囲を通知関数に渡します。なお、初回の解析も「変更」とみなされるため、初回解析時にも通知関数が呼び出され、範囲はバッファ全体となります。

関数: treesit-parser-add-notifier parser function

この関数は、parser の変更後通知関数リストに function を追加します。function はラムダ関数ではなく、関数シンボルである必要があります(匿名関数を参照)。

関数: treesit-parser-remove-notifier parser function

この関数は、parser の変更後通知関数のリストから function を削除します。function はラムダ関数ではなく、関数シンボルである必要があります。

関数: treesit-parser-notifiers parser

この関数は、parserの通知関数の一覧を返します。

Lispプログラムでは、パーサーに再解析を強制させ、treesit-parser-changed-regionsを用いて変更箇所を即座に取得することも可能です。

関数: treesit-parser-changed-regions parser

この関数は parser の再解析を強制し、影響を受けた領域((start . end) のリスト)を返します。パーサーに再解析すべき新しい箇所がない場合、または影響を受けた領域が空である場合、この関数は nil を返します。

他言語用の代替パーサー

言語Bの文法が、別の言語Aの文法の厳密な上位集合となる場合があります。その場合、言語Aの設定(font-lockルール、インデントルールなど)を言語Bに再利用するのが合理的です。そのために、treesit-language-remap-alistではユーザーが言語Aを言語Bに再マッピングできるようになっています。

変数: treesit-language-remap-alist

この変数の値は、(language-a . language-b) のアリストである必要があります。アリスト内にそのようなペアが存在する場合、language-a 用のパーサーを作成すると、実際には language-b 用のパーサーが作成されます。同様に、ノードを作成したり language-a へのクエリを発行したりするあらゆる処理は、代わりに language-b を使用するようにリダイレクトされます。このマッピングは完全に透過的であり、作成されたパーサーは language-b を使用していると報告され、このパーサーによって作成されたノードについても同様です。

具体的には、treesit-parser-create によって生成されたパーサーは、渡された language をそのまま使用することを報告します。例えば、言語 cpp が cuda にマッピングされている場合:

(setq treesit-language-remap-alist '((cpp . cuda)))

(treesit-parser-language (treesit-parser-create 'cpp))
  ⇒ 'cpp

(treesit-parser-language (treesit-parser-create 'cuda))
  ⇒ 'cuda

どちらのパーサーも実際にはcudaパーサーであるにもかかわらず。