-
vertico.elのバージョン1.10のREADME.org日本語訳です
-
ライセンス: パッケージのライセンスと同じ、すなわちGPLv3.0ライセンスです
Table of Contents
Verticoはデフォルトの補完システムに基づき効率的かつミニマリズム的な垂直補完ユーザーインターフェイスを提供する。Vertico焦点を当てているのは、すべての状況下において正しく振る舞うユーザーインターフェイスを提供することだ。ビルトイン機能システムの再利用することで、VerticoはEmacsのビルトイン補完コマンドや補完テーブルとの完全な互換性を実現した。Verticoが提供するのは補完ユーザーインターフェイスだけではあるが柔軟性、拡張性、モジュール性を高めるということも目標としている。拡張性についてはextensionsやcomplementary packagesを利用して追加できる。コードベースは小さく保守性にも優れる。メインとなるvertico.elパッケージは空白やコメントを除けばおよそ600行程度に過ぎない。
機能
- 矢印キーを使ったナビゲーションによる垂直表示、他の表示モードもextensionsとして追加で多数用意されている
- カレント候補のインデックスと候補の総数をプロンプトに表示
-
TABでカレント候補の挿入、選択はRET - 入力プロンプトにポイントを移動、または
M-RETで既存にはない候補の入力が可能 - ヒストリー(history: 履歴)内での位置、長さ、アルファベット順での効率的なソート
- 改行を含む長い候補の表示幅を減少させるフォーマット処理
- lazy補完で候補をハイライトすることによる性能向上
- 候補の隣に表示される注釈(
annotation-およびaffixation-function) - 候補のグルーピングとグループ巡回コマンド(
group-function)のサポート
インストール
VerticoはGNU ELPAから入手できる。M-x package-install RET vertico RETで直接インストール可能だ。インストール後に、M-x vertico-mode RETでグローバルマイナーモードをアクティブにすればよい。
キーバインディング
Verticoはミニバッファー用にminibuffer-local-mapから継承したローカルキーマップを独自に定義している。このキーマップではfundamental-modeのキーバインディングがほとんど手付かずで残されており、いくつかのコマンドをリマップ(remap: 再マップ)やバインドしているだけだ。
| バインディング/リマップ | Verticoコマンド |
|---|---|
beginning-of-buffer, minibuffer-beginning-of-buffer
|
vertico-first |
end-of-buffer |
vertico-last |
scroll-down-command |
vertico-scroll-down |
scroll-up-command |
vertico-scroll-up |
next-line, next-line-or-history-element
|
vertico-next |
previous-line, previous-line-or-history-element
|
vertico-previous |
forward-paragraph |
vertico-next-group |
backward-paragraph |
vertico-previous-group |
exit-minibuffer |
vertico-exit |
kill-ring-save |
vertico-save |
M-RET |
vertico-exit-input |
TAB |
vertico-insert |
カレントで選択されている候補を挿入するvertico-insertにバインドされているTAB、そしてvertico-exit、vertico-exit-inputにバインドされているRETとM-RETには特に注意して欲しい。
vertico-exitはカレントで選択された候補で補完を終了するが、vertico-exit-inputは候補をミニバッファーに入力して補完を終了する。switch-to-bufferで新しいバッファーを作成したり、find-fileで新たにファイルを作成したいときには、カレントの入力で補完を終える手段が必要になるのだ。M-RETを押下するかわりに矢印キーupで選択を入力プロンプトまで移動してからRETの押下でもよい。
構成
"init.el"でVerticoやその他パッケージを構成する場合には、use-packageのメリットを享受したいと思うかもしれない。以下に構成の例を示そう:
;; Verticoを有効化
(use-package vertico
:custom
;; (vertico-scroll-margin 0) ;; スクロールマージンの変更
;; (vertico-count 20) ;; 表示する補完の数を増やす
;; (vertico-resize t) ;; Verticoミニバッファーの拡大、縮小
;; (vertico-cycle t) ;; `vertico-next/previous'の巡回を有効化
:init
(vertico-mode))
;; Emacsの再起動を跨いでヒストリーを永続化
;; Verticoはヒストリー位置によってソートを行う
(use-package savehist
:init
(savehist-mode))
;; 更に便利な構成をいくつか...
(use-package emacs
:custom
;; 既存ミニバッファー内部からの新たなミニバッファーのオープンをサポート
(enable-recursive-minibuffers t)
;; カレントモードでは動作しないコマンドをM-xのコマンドから除外
;; 通常のバッファーではVerticoのコマンドは表示されなくなる
;; Verticoに限らず役に立つセッティングではないだろうか
(read-extended-command-predicate #'command-completion-default-include-p)
:init
;; `completing-read-multiple'にプロンプトのインジケーターを追加する
;; [CRM<separator>]のような表示になる
;; たとえばseparatorがカンマなら[CRM,]
(defun crm-indicator (args)
(cons (format "[CRM%s] %s"
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
;; ミニバッファープロンプトへのカーソル移動の禁止
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))
試しにOrderless補完を与えてみることをお勧めする。これはデフォルトの補完スタイルに比べて柔軟かつ強力な補完スタイルだ。
;; オプションで`orderless'補完スタイルを使用
(use-package orderless
:custom
;; カスタムスタイルのdispatcherを構成(Consultのwikiを参照)
;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
;; (orderless-component-separator #'orderless-escapable-split-on-space)
(completion-styles '(orderless basic))
(completion-category-defaults nil)
(completion-category-overrides '((file (styles partial-completion)))))
orderlessに加えてフォールバックとして補完スタイルbasicを指定する理由は、たとえばcompletion-table-dynamicやcompletion-table-in-turnのような動的な補完テーブルに依存する補完コマンドが正しく機能することを保証するためだ。スタイルディスパッチャによる、より高度なOrderless構成についてはConsult wikiを参考にして欲しい。更にファイル名の展開用にpartial-completionを有効にしている。partial-completionはfind-fileにおけるファイル名のワイルドカードサポートにとっては重要だ。複数のファイルをワイルドカードで一度にオープンするためには、プロンプトでM-RETを入力する必要がある。かわりにまずプロンプトに移動してからRETの押下でもよい。
追加の構成についてのヒントについてはVertico Wikiも参考にして欲しい。より一般的なドキュメントについては、Emacsマニュアルの補完のチャプターに目を通すのもお勧めだ。自分で補完コマンドを作成したい場合には、Elispマニュアルでも補完に関する文書を見つけることができるだろう。
補完スタイルとタブ補完
minibuffer-local-completion-mapのバインディングは、Verticoではデフォルトでは利用できない。これはTABがBashのようなシェルやEmacsのデフォルトの補完システムなどから期待されるるような動きをしないことを意味している。VerticoにおけるTABはカレントで選択されている候補の挿入を行うのだ。
他の補完コマンドが利用できるようにしたければ新たにバインディングを追加したり、あるいはVerticoのバインディングの置き換えさえできる。たとえばM-TABで補完のプレフィックスを展開したい(いわゆるTAB補完だ)、あるいはcompletion-cycle-thresholdが非nilの場合には補完を巡回したければ以下の構成がよいだろう。
;; オプション1: 追加のバインディング
(keymap-set vertico-map "?" #'minibuffer-completion-help)
(keymap-set vertico-map "M-RET" #'minibuffer-force-complete-and-exit)
(keymap-set vertico-map "M-TAB" #'minibuffer-complete)
;; オプション2: `vertico-insert'ではなくTABでプレフィックス展開に置き換え
;; (keymap-set vertico-map "TAB" #'minibuffer-complete)
コマンドminibuffer-completeは補完スタイルbasicにたいしてプレフィックス展開を行い、補完スタイルのorderlessとsubstringは補完の一番長い部分文字列の展開を行う。展開やフィルタリングを行うpartial-completion、flex、initialsといった他の補完スタイルを使うこともできるだろう。ワイルドカードを使用してfind-fileで一度に複数のファイルをオープンしたければ、partial-completionも重要な選択肢だ。ワイルドカードで一度に複数ファイルをオープンするためには、プロンプトでM-RETを入力する必要がある。最初にプロンプトに移動してからRETの押下でもよいだろう。
(setq completion-styles '(basic substring partial-completion flex))
VerticoはEmacsのデフォルトの補完システムと完全な互換性があるので、Emacsの準公式変数をセットすることで、補完の振る舞いの更なるカスタマイズを実現できる筈だ。たとえばビルトインの補完システムを使用しているときには、大文字小文字の区別を無効にしたいと思ったら以下のようにすればよい:
(setq read-file-name-completion-ignore-case t
read-buffer-completion-ignore-case t
completion-ignore-case t)
Completion-at-pointとcompletion-in-region
タブ補完コマンドcompletion-at-pointは通常だとM-TABやTABにバインドされている。タブ補完はミニバッファーにおいてもM-: (eval-expression)によって使用されている。このような場合にcompletion-at-pointやcompletion-in-regionの補完候補表示にVerticoを使いたければ、Consultパッケージ提供の関数consult-completion-in-regionを使用できる。
;; Verticoが有効なら`consult-completion-in-region'を使用、
;; それ以外はデフォルトの`completion--in-region'関数を使用
(setq completion-in-region-function
(lambda (&rest args)
(apply (if vertico-mode
#'consult-completion-in-region
#'completion--in-region)
args)))
わたしのCorfuパッケージも調べてみたいと思うかもしれない。これは子フレームをポップアップしてcompletion-in-region用のミニマリズム的な補完システムを提供するパッケージだ。Corfuのほうはパッケージに焦点を絞っているが、Verticoと同じ精神で開発している。ミニバッファーでCorfuを使うことすら可能だ。
拡張
このレポジトリではextensions/というサブディレクトリーで、Vertico用に小さな拡張パッケージを保守している。ELPAからパッケージを取得した場合には、Verticoと一緒に拡張のインストールができる。拡張はデフォルトではアクティブになっていないが、もしアクティブにしたければ手作業で有効にできる。更にvertico.elとvertico-*.elといった拡張のファイルはすべて個別にインストールすることができるのだ。現在のところELPAのVerticoパッケージには以下の拡張が同梱されている:
-
vertico-buffer:
vertico-buffer-modeは通常のバッファーのようにVerticoを表示する -
vertico-directory: Ido風にディレクトリーをナビゲーションするコマンド
-
vertico-flat:
vertico-flat-modeはフラットで水平方向の表示を有効にする -
vertico-grid:
vertico-grid-modeはグリッド表示を有効にする -
vertico-indexed:
vertico-indexed-modeはプレフィックス引数でインデックス付けされた候補を選択できる -
vertico-mouse:
vertico-mouse-modeはスクロールと候補選択のサポート -
vertico-multiform: コマンドごと、あるいは補完カテゴリーごとにVerticoモードを構成する
-
vertico-quick: Avyスタイルのクイックキーを用いて選択を行うコマンド
-
vertico-repeat:
vertico-repeatは最後の補完セッションを繰り返すコマンド -
vertico-reverse:
vertico-reverse-modeは表示の反転 -
vertico-suspend:
vertico-suspendはカレントセッションのサスペンドとリストアを行うコマンド -
vertico-unobtrusive:
vertico-unobtrusive-modeは一番上の候補だけを表示する
構成の詳細についてはこれらのファイルのコメントを確認して欲しい。これらの拡張を用いればあなたの好みに合うように、あるいは他の類似UIと同様の挙動になるようにVerticoを改造できるだろう。たとえばvertico-flatにvertico-directoryを追加して組み合わせれば、Idoのルック&フィールに似せられる。Helmに似せたインターフェイスであれば、vertico-bufferの拡張を使ってミニバッファーを広げるかわりに補完バッファーをどこにオープンするか自由に構成できるだろう。更にvertico-bufferならバッファーの高さに応じて候補の表示数を調節できるのだ。
以下にvertico-directory用の構成を示す:
;; ディレクトリー拡張の構成
(use-package vertico-directory
:after vertico
:ensure nil
;; より便利にディレクトリーをナビゲーションするコマンド
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word))
;; ミニバッファーのシャドー部分でファイル名を整える
:hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
コマンドごとあるいはカテゴリごとにVerticoを構成する
Verticoが提供するvertico-multiform-modeではコマンドごと、あるいは補完カテゴリーごとにVerticoを構成できる。vertico-buffer-modeではHelm風のバッファー表示が利用できるので、スペースの消費は増えるがより多くの補完を表示できる。この冗長な表示モードはカレントバッファー全体に渡ってより良い概観を得られるバッファー表示なので、consult-imenuやconsult-outlineのようなコマンドで役に立つだろう。しかしそれ以外のコマンドではVerticoのデフォルト表示を使い続けたいと思うかもしれない。この構成問題を解決するのがvertico-multiform-modeだ。
;; vertico-multiformを有効にする
(vertico-multiform-mode)
;; コマンドごとに表示を構成する
;; imenuの表示にはバッファーを使い
;; M-xには(Ido風の)フラットなメニューを使用する
(setq vertico-multiform-commands
'((consult-imenu buffer indexed)
(execute-extended-command unobtrusive)))
;; コマンドカテゴリーごとに票を構成する
;; ファイルにはグリッド表示を使い
;; consult-grepにはバッファー表示を使用する
(setq vertico-multiform-categories
'((file grid)
(consult-grep buffer)))
異なる表示モード間を一時的に切り替えることができる。以下のコマンドはvertico-multiform-mapにデフォルトでバインドされている。もちろんお望みならバインディングの変更も可能だ。
-
M-B->vertico-multiform-buffer -
M-F->vertico-multiform-flat -
M-G->vertico-multiform-grid -
M-R->vertico-multiform-reverse -
M-U->vertico-multiform-unobtrusive -
M-V->vertico-multiform-vertical
コマンドごと、あるいは補完カテゴリーごとに補完の振る舞いを構成するために、特別構成として独自の関数、あるいはラムダ式さえ使うことができる。関数はモードの呼び出し規約にしたがわなければならない。つまり1つの引数を受け取り、その値が1ならモードをオンに、-1ならモードをオフに切り替える関数だ。
;; 別バッファーにスケールダウンされたTOCとして`consult-outline'を構成
(setq vertico-multiform-commands
`((consult-outline buffer ,(lambda (_) (text-scale-set -1)))))
さらにバッファーローカルにコマンドごと、あるいは補完カテゴリーごとにチューニングすることもできるのだ。
;; デフォルトのソート関数を変更
;; `vertico-sort-function'および`vertico-sort-override-function'を参照
(setq vertico-multiform-commands
'((describe-symbol (vertico-sort-function . vertico-sort-alpha))))
(setq vertico-multiform-categories
'((symbol (vertico-sort-function . vertico-sort-alpha))
(file (vertico-sort-function . sort-directories-first))))
;; ファイルの前にソートしたディレクトリー
(defun sort-directories-first (files)
(setq files (vertico-sort-history-length-alpha files))
(nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) files)
(seq-remove (lambda (x) (string-suffix-p "/" x)) files)))
3つの機能を組み合わせることで、vertico-buffer-display-actionより以上に補完表示を精密に調節できる。たとえばconsult-grepカテゴリーのコマンド(consult-grep、consult-git-grep、consult-ripgrep)にたいしてカレントウィンドウを再利用するといったことが可能になる。この構成はConsultのプレビューとは互換性がないことに注意。これはプレビューバッファーが正確にそのウィンドウに表示されるからだ。とはいえこのスニペットで構成の柔軟度については判ってもらえたと思う。
;; バッファー表示とバッファーディスプレイアクションを構成
(setq vertico-multiform-categories
'((consult-grep
buffer
(vertico-buffer-display-action . (display-buffer-same-window)))))
;; consult-grepコマンドのプレビューは無効化
(consult-customize consult-ripgrep consult-git-grep consult-grep :preview-key nil)
別の例として、以下はvertico-flatとvertico-cycleを用いて(ido-mode 'buffer) (バッファー名の補完にたいしてのみIdoを有効化)をエミュレートするコードを示そう。スクリーンを左にスクロールオフした際に補完候補が見えなくなることを防ぐために、ここではvertico-cycleにtをセットする必要がある。
(setq vertico-multiform-categories
'((buffer flat (vertico-cycle . t))))
補足的なパッケージ
Verticoには豪華な補完UIをもつ補足パッケージが統合されている。以下は完全にサポートされているパッケージだ:
-
Marginalia: ミニバッファーでの贅沢な注釈
-
Consult: 使いやすい検索やナビゲーション用のコマンド
-
Embark: ミニバッファーアクションとコンテキストメニュー
-
Orderless: 先進的な補完スタイル
このパッケージエコシステムに馴染むためのアプローチとして以下のクイックスタートをお勧めする:
-
Emacsを素で起動(
emacs -Q) -
Verticoをインストールしてインクリメンタルなミニバッファー補完を有効にする
-
Orderlessのインストールおよび/またはビルトインの補完システムを構成して、ミニバッファーでのフィルタリングをより柔軟にする
-
豪華なミニバッファーの注釈がお好みならMarginaliaをインストール
-
Embarkをインストールして
embark-dwim``embark-actのためにキーバインディングを2つ追加(これらのコマンドによってポイント位置やミニバッファーにあるオブジェクトにアクションできるので、わたしは覚えやすいようにキーバインディングとしてM-.とC-.を割り当てている) -
機能豊富な補完コマンド(たとえばプレビュー付きのバッファー切り替えには
consult-buffer、行志向の検索ならconsult-lineなど)を追加したければConsultをインストール -
consult-lineからoccur-modeバッファーへのエクスポートや、consult-grepから編集可能なgrep-modeへエクスポートするなら、WgrepとEmbark-Consultをインストールしよう -
そしてextensionsを使ってVerticoを更に細かくチューニングしていく
このエコシステムはモジュール化されている。これらのコンポーネントすべてを使う必要はない。あなたが気に入ったパッケージ、あなたのセッティングによく馴染むパッケージだけを使おう。1.から4.は素のEmacsに新たなコマンドを導入するステップ、ステップ5.で新たなコマンドembark-actとembark-dwimも導入、ステップ6.でConsultのコマンドが手に入る。Emacsにまだない機能を提供するコマンド(consult-lineとか)があるし、置き換えるコマンド(switch-to-bufferのconsult-bufferによる置き換え)もある。
子フレームとポップアップ
よくあるリクエストが子フレームをポップアップして補完できる機能だろう。個人的にはミニバッファー補完に子フレームを使うことには批判的である。経験的には解決されるより多くの問題が引き起こされたからだ。もっとも重要なのは子フレームが背後のバッファーコンテンツを隠してしまう点にある。更に子フレームはウィンドウの変更や再帰的なミニバッファーセッションとの相性が良くない。その上に子フレームだと遅く感じたり、ちらつくこともあり得る。vertico-bufferでの表示がよい代替えだと思う。こちらはvertico-multiformを使えばコマンドごとに個別に構成することさえ可能だ。子フレームの利点は視線が焦点を結ぶスクリーン中央に補完が表示される点だろうか。以下のパッケージを試してあなた自身で判断して欲しい。
-
mini-frame: 子フレームにミニバッファー全体を表示
-
mini-popup: 若干シンプルなミニフレームの代替品
-
vertico-posframe: posframeライブラリーを使って子フレームにverticoミニバッファーだけを表示
他の選択肢
補完UIには多くの選択肢があり、UIごとに利点と欠点が存在する。
Verticoが目指すのはcompleting-readに信を置くことで独自APIの作成を避けて、最小限のコードベースでEmacsのコマンドすべてに100%準拠するというゴールだ。パッケージの柔軟性と再利用度を向上させるために、HelmやIvyのようなカスタムAPIの発明は意識的に避けている。このパッケージ自身の小さなコードベースとEmacs機能の再利用のお陰で、補完UIやモノリシックな補完システムと比較するとバグや互換性問題が発生しにくくなっている。
Verticoが提供するのはユーザーインターフェイスだけなので、他の何らかの補完パッケージと組み合わせることによりHelmやIvyに似たフル機能の補完体験を提供できるようにしたいと思うかもしれない。これはより小さな独立したコンポーネントをもつことによって、ステップ&ステップでコンポーネントの追加と理解ができるというアイデアである。コンポーネントはそれぞれ可能な限り出しゃばらないように努めて自分の得意分野に注力するのだ。これらのコンポーネントと、ユーザーがEmacsと対話する手法において欠かせない役を演じる補完をリンクさせて、自分のEmacsを細部に渡り正確に作り出すことに興味があるユーザーこそ、Verticoがターゲットとするユーザーだ。
似たような哲学にしたがう対話的な補完UIが他にも存在する:
-
Mct: ミニバッファーと補完の連携。Mctは候補選択、およびミニバッファーと補完バッファー間の移動を行うために、自動更新やキーバインディングを追加してデフォルトの
*Completions*バッファーを強化して再利用している。Mctが使用するのはバッファーとして完全な機能をもった補完バッファーなので、バッファー内で慣れ親しんだバッファーコマンドを使用できるだろう。Verticoのアプローチとの主な違いは、*Completions*バッファーにマッチするすべての候補が表示される点だ。候補と自由にやり取りができ、IsearchやAvyで飛び回ることができることも利点だ。その一方で速度は必然的に低下してしまう。 -
Selectrum: Verticoの前身であるSelectrumは、Verticoを支持する声によって時代遅れとなった。Selectrumから移行する際にはmigration guideを一読して欲しい。
Selectrumの技術的な欠点に対処するよう特に設計されたのがVerticoである。 SelectrumはEmacsのすべてのコマンドと動的テーブルへの完全な互換性はもっていない。なぜならフィルタリングにおいてEmacsの標準的な補完機能から逸脱した独自のインフラストラクチャを使用しているせいだ。 -
Icomplete: Emacsにはビルトインの
icomplete-vertical-modeが同梱されているが、Verticoより更に必要最小限の機能しかもっていない。Verticoの方はextensionsのお陰で、さらなる柔軟性を提供できるのだ。
リソース
Verticoやミニバッファー補完についてもっと学びたければ、以下のリソースをチェックして欲しい:
-
補完にたいしてVerticoCorfuを使用する構成:
-
ビデオ:
- Emacs Completion Explained (2022-07-19) Andrew Tropin氏による
- Emacs Minibuffer Completions (2022-02-12) Greg Yut氏による
- Vertico Extensions for Emacs (2022-01-08) Karthik Chikmagalurによる
- Using Emacs Episode 80 - Vertico, Marginalia, Consult and Embark (2021-10-26) Mike Zamanskyによる
- System Crafters Live! - Replacing Ivy and Counsel with Vertico and Consult (2021-05-21) David Wilsonによる
- Streamline Your Emacs Completions with Vertico (2021-05-17) David Wilsonによる
- Modern Emacs: all those new tools that make Emacs better and faster (2024-03-06) Marie-Hélène Burleによる
貢献するには
このパッケージはGNU ELPAの一部なので、貢献においてはFSFの著作権の譲渡が必要になる。
Verticoのデバッグ
post-command-hookでvertico--exhibitのエラーを発見したら、強制的にデバッグできるようにadviceをインストールするべきだ。これによりエラー発生箇所を絞り込むためのスタックトレースが得られるだろう。理由はEmacsによってpost-command-hookが自動的に無効化されることにある(そしてデバッグされない)。すべてのコマンドの後にフックが実行された場合を考えれば、そうしないとEmacsが使い物にならなくなることが判るだろう。
(setq debug-on-error t)
(defun force-debug (func &rest args)
(condition-case e
(apply func args)
((debug error) (signal (car e) (cdr e)))))
(advice-add #'vertico--exhibit :around #'force-debug)
問題の発生しがちな補完コマンド
ほとんどのシナリオにおいてVerticoは堅牢だ。ただし一部の補完コマンドの中には、補完のスタイルやユーザーインターフェイスにたいして何かを決めてかかるものが存在する。Verticoやその他のUIはこれらの先入観をもっていないものの、軽微な対処が必要な場合もあるかもしれない。
org-refile
org-refileはorg-refile-use-outline-pathが非nilだと、アウトラインパス(訳注: アウトライン構造をディレクトリーのパスのように表現したもの)の補完ステップの中でorg-olpath-completing-readを使用する。
残念ながらこのOrgの補完テーブルは、補完スタイルにbasicが使用されている前提で実装されている。これはsubstring、flex、orderlessといった補完スタイルと互換性がない。この問題に根本的に対処するためには、ビルトインのファイル補完テーブルのように補完テーブルに補完境界(completion boundary)を使用させる必要があるだろう。構成で対処する場合にはorderlessよりbasicの優先度を高くすればよい。
;; 選択肢その1: 補完スタイルbasicを使う
(setq org-refile-use-outline-path 'file
org-outline-path-complete-in-steps t)
(advice-add #'org-olpath-completing-read :around #'vertico-enforce-basic-completion)
(defun vertico-enforce-basic-completion (&rest args)
(minibuffer-with-setup-hook
(:append
(lambda ()
(let ((map (make-sparse-keymap)))
(define-key map [tab] #'minibuffer-complete)
(use-local-map (make-composed-keymap (list map) (current-local-map))))
(setq-local completion-styles (cons 'basic completion-styles)
vertico-preselect 'prompt)))
(apply args)))
アウトラインパスにたいする補完のステップで補完を無効にしたいと思うかもしれない。フルパスにたいして補完すれば、フルパスの部分文字列にたいして入力文字列を直接マッチさせるので高速になる筈だ。ただし適用可能な補完はより乱雑になるだろう。
;; 選択肢その2: フルパスを補完する
(setq org-refile-use-outline-path 'file
org-outline-path-complete-in-steps nil)
org-agenda-filterとorg-tags-view
コマンドorg-agenda-filterおよびorg-tags-viewは、org-refileと同じように補完境界を使用しない。内部的な補完テーブルはorg-agenda-filter-completion-functionとorg-tags-completion-functionだ。この理由によりsubstring、flex、orderlessのような自由裁量の補完スタイルには、残念ながらTAB補完(minibuffer-complete)が機能しない。これはVerticoだけではなく、Emacsのデフォルトの補完システムにも影響を及ぼす。たとえば+tag<0 TABと入力すると0:10に置き換えられるが、これは間違いだ。補完境界に保護されていれば、結果は+tag<0:10になっただろう。たとえばファイル補完に使用されている補完境界は、パスの部分それぞれを別個に補完するために使用されている。理屈ではOrgで以下のようにすればこの問題は解決する筈だ。
(advice-add #'org-make-tags-matcher :around #'vertico-enforce-basic-completion)
(advice-add #'org-agenda-filter :around #'vertico-enforce-basic-completion)
tmm-menubar
注意: わたしは以下の問題にたいする修正を実装した。Emacs 31に含まれることになるだろう。構成でcompletion-eager-displayにnilをセットするのだ。アップストリームのバグレポートについてはbug#74616を参照のこと。
Verticoでもテキストメニューバーは良好に機能する。しかし常に*Completions*を表示することについては、Verticoを使っていれば望ましくない点だろう。adviceすることで補完バッファーは今すぐ無効にできる。標準的なGUIのメニューバーを無効にしていて、かつVerticoのインターフェイスが気に入ったのであれば、F10にたいするデフォルトのキーバインディングも上書きしたいと思うかもしれない。
(keymap-global-set "<f10>" #'tmm-menubar)
(advice-add #'tmm-add-prompt :after #'minibuffer-hide-completions)
ffap-menu
注意: わたしは以下の問題にたいする修正を実装した。Emacs 31に含まれることになるだろう。構成でcompletion-eager-displayにnilをセットするのだ。アップストリームのバグレポートについてはbug#74616を参照のこと。
コマンドffap-menuはデフォルトではtmm-menubarと同じように*Completions*を表示するが、Verticoを使っているのであれば望ましくないと思う。以下のようにして補完バッファーを無効にできる。
(advice-add #'ffap-menu-ask :around
(lambda (&rest args)
(cl-letf (((symbol-function #'minibuffer-completion-help)
#'ignore))
(apply args))))
completion-table-dynamic
動的な補完テーブル(completion-table-dynamic、completion-table-in-turn、...)にたいしてVerticoは良好に機能する筈だ。唯一必要なのは補完スタイルbasicが有効になっていることだけである。basicスタイルは入力を補完テーブル(または動的補完テーブル関数)に入力を渡すことで、プレフィックスのフィルタリングを処理している。補完スタイルとしてbasicが最高の優先度で構成されている必要はない。orderless、substring、flexのような補完の補完スタイルより低い優先度で問題ない。これはOrderlessのドキュメントで推奨されているように、completion-table-dynamicのためだ。
(setq completion-styles '(basic))
;; (setq completion-styles '(orderless basic))
(completing-read "Dynamic: "
(completion-table-dynamic
(lambda (str)
(list (concat str "1")
(concat str "2")
(concat str "3")))))
空文字列の入力
multi-occur、auto-insert、bbdb-createといったコマンドはcompleting-readを用いてミニバッファーから複数の引数を読み取る。空の文字列が入力されるまで1回ずつ読み取るのだ。このループを終了させるにはM-RET (vertico-exit-input)をタイプするべきだろう。最初の候補が事前選択されているので、直接RET (vertico-exit)を押下しても機能しないのだ。
ループから抜け出すためにcompleting-readが常に空文字列を許容するという点に問題が隠れている。これはたとえ引数REQUIRE-MATCHが非nilであったとしても、*NULL補完(null completion)*と呼ばれる補完なのだ。C-x C-eで以下の2つのcompleting-readを呼び出してみよう:
(completing-read "Select: " '("first" "second" "third") nil 'require-match)
(completing-read "Select: " '("first" "second" "third") nil 'require-match nil nil "")
どちらの場合でも空文字列が入力される。1つ目はデフォルトの値が明示的に指定されておらず、Verticoが事前に1つ目の候補を選択している場合だ。空文字列で終えるためにはM-RETを押下する必要がある。2つ目ではデフォルトの値として""が明示的に指定されているのでVerticoが事前に選択するのはプロンプトだ。これならRETを押下するだけで空文字列で終えることが可能である。
Trampでのhostname/usernameの補完
注意: Emacs 29.2とTramp 2.7では関連する補完テーブルが改良されたので、このセクションに記した回避策は不要になった。
Orderlessやsubstring、flexといった非プレフィックスな補完スタイルでは、/ssh:を入力した後はホスト名やユーザー名で補完が利用できなくなる。この問題を回避するためには、ファイルの補完カテゴリーでorderlessの前にbasicが試行されるようにbasic補完スタイルを指定する必要がある。これはファイルの補完カテゴリーをオーバーライドするように、補完スタイルの最初にbasicを配置することで実現できるだろう。
(setq completion-styles '(orderless basic)
completion-category-defaults nil
completion-category-overrides '((file (styles basic partial-completion))))
completion-styleの仕組みに馴染みがあれば、リモートのファイルにたいしてのみアクティブになるカスタム補完テーブルの定義もできる。このカスタム補完スタイルでリモート以外のファイル名の一部にマッチするようにすれば、リモート以外のファイルにたいしては依然としてorderlessスタイルの優先が維持されるだろう。
(defun basic-remote-try-completion (string table pred point)
(and (vertico--remote-p string)
(completion-basic-try-completion string table pred point)))
(defun basic-remote-all-completions (string table pred point)
(and (vertico--remote-p string)
(completion-basic-all-completions string table pred point)))
(add-to-list
'completion-styles-alist
'(basic-remote basic-remote-try-completion basic-remote-all-completions nil))
(setq completion-styles '(orderless basic)
completion-category-defaults nil
completion-category-overrides '((file (styles basic-remote partial-completion))))

