大昔の事しか知らなかったから、最近の仕組みまわりをなんとなく調べてみたメモ。Emacs 28.1。
補完を支える基本は昔と変わってなくて all-completions
と try-completion
。引数も Emacs 20 の昔と変わってない模様。
completing-read
等をしたときに M-TAB
で発動する補完は minibuffer-complete
。
補完の為の値は minibuffer-completion-table
や minibuffer-completion-predicate
などのバッファローカル変数で持っている。
補完方法のリストを completion-styles
でユーザーが定義できるようになっている。
completion-styles
を意識した補完の関数は completion-all-completions
と completion-try-completion
。また、ソートして結果を返すための completion-all-sorted-completions
が準備されている。
これらの関数は completion-all-completions
は completion-styles
を順に試して候補が見つかったらそれを返す。
これらの補完関数を使った場合、completion table が関数 (プログラム補完) ならば completion-metadata
を使ってその候補におけるメタデータを取得できる。このメタデータを使ってソート順などを表現するようになっている。
completion-category-overrides
を設定する事で、コンテキストに合わせた補完スタイルが選択できる。このキーはメタデータ category
で指定される。
各スタイルは completion-styles-alist
に定義されている。
(completion-all-completions string table pred point &optional metadata)
point
はカーソルの位置。また、引数 metadata
でメタデータを上書きできる。
返り値に注意で、リストが返ってくるんだけど、最後のコンセルの cdr が nil じゃない事がある。base-size と呼ばれていて、共通部分をハイライトするのに使われている。
つまり、普通の候補一覧が欲しいときは以下のようにする。
(let ((collection '("fuga_hoge" "hoge_fuga" "hoge"))
(completion-styles '(partial-completion)))
(let ((candidates (completion-all-completions "hoge" collection nil 4)))
(when-let (last (last candidates)) (setcdr last nil))
candidates))
補完関数に渡す completion table はコレクションだけではなく関数も渡せる。この辺は昔と変わらない。
プログラム補完の関数を便利に作るための completion-table-dynamic
や completion-table-with-cache
が準備されている。
プログラム補完するには (funcall table string predicate t)
のように呼ぶ。
補完される側にとってはカスタマイズ性が高くてよいのだけど、補完する側にとっては自由にマッチさせられない事になるので、わりと困りものな気もする。
プログラム補完の関数を (funcall table string predicate 'metadata)
のように呼ぶ事でメタデータを取得できる。
メタデータの種類は以下の通り (completion-metadata
の docstring から引用)
category
: the kind of objects returned byall-completions
. Used bycompletion-category-overrides
.annotation-function
: function to add annotations in *Completions*. Takes one argument (STRING), which is a possible completion and returns a string to append to STRING.affixation-function
: function to prepend/append a prefix/suffix to entries. Takes one argument (COMPLETIONS) and should return a list of annotated completions. The elements of the list must be three-element lists: completion, its prefix and suffix. This function takes priority overannotation-function
when both are provided, so only this function is used.group-function
: function for grouping the completion candidates. Takes two arguments: a completion candidate (COMPLETION) and a boolean flag (TRANSFORM). If TRANSFORM is nil, the function returns the group title of the group to which the candidate belongs. The returned title may be nil. Otherwise the function returns the transformed candidate. The transformation can remove a redundant prefix, which is displayed in the group title.display-sort-function
: function to sort entries in *Completions*. Takes one argument (COMPLETIONS) and should return a new list of completions. Can operate destructively.cycle-sort-function
: function to sort entries when cycling. Works likedisplay-sort-function
.
なんと group-function
なんて物まで存在していた。進化している。
補完システムはこれらを使っていい感じに表示してくれる。
一部の属性は、存在しない場合 completion-extra-properties
を使って代替できる。
company では追加のメタデータを導入している。company が覇権を取っていた時期が長かったのもあって、これらの属性はほぼ業界標準になっている (ように見える)。 company 以外の補完システムでもこのテキストプロパティは使われていて、なんと elisp-mode が作る補完候補にも含まれている。
使われているのは
company-docsig
company-doc-buffer
company-location
company-kind
company-prefix-length
など。
WIP
WIP
WIP
WIP
WIP