Embarkを使う15の方法

Author: ayatakesi
GitHub Source: md

これはKarthik Chikmagalurさんによって記述された記事を日本語に翻訳した記事であり、記事の所有権と著作権はKarthik Chikmagalurさんに帰属します。

元の記事: Fifteen ways to use Embark | Karthinks

Embarkは非常に優れたパッケージであり、思慮深く設計されたパッケージでもある。Emacsのアクション -> オブジェクトという順序を反転させるパッケージであり、学習曲線の追加なしで習得できる。EmbarkはわたしのEmacsの使い方を完全に変えてしまったんだ。この記事でその理由について説明しよう。

Emacsのアクションモデルは、デフォルトではアクションを指定(たとえばfind-file)した後にオブジェクト(たとえばファイル)を指定する:

emacs-pattern.png

これはシェルで何か物事を行う方法を反映したモデルだ:

shell-pattern.png

違いはシェルコマンドのほうは単なるテキスト行なので、実行前であればアクションとオブジェクトはどちらも自由に編集できることだ。Emacsではオブジェクトは自由に変更できても、アクションを変更するにはC-gを押下してから別のコマンドの呼び出しが必要となる。

ファイルマネージャーのようなGUIプログラムでは別の方法によって物事が運ぶ。オブジェクト表現(通常はアイコン)を選択して、それにたいして実行したいアクションを選択するのだ:

gui-pattern.png

どのパラダイムでも良好に機能する。しかしこちらはEmacsだ。ただ1つの方法を選択する謂われなど存在しない! Embarkによって2つのパターンの間を行き来することが可能になる 。たとえばファイルを別のウィンドウでオープンしたい? 別のディレクトリーにコピーしたい? 自分が何がしたいのかを決める前に、まずはfind-fileを呼び出してファイルを選ぶといった操作が可能になるのだ。

embark-pattern.png

Embarkがあれば容易いことだ。

embark-act: 実は… & でもまずは…

あなたにとって”実は…”を実現するコマンドがembark-actだ。つまりpackage-installを呼び出してパッケージを選んだものの、実はパッケージの説明が読みたかったんだ! を実現するコマンドと言える。

同時にembark-actは”ああ、でもまずは…”を実現するコマンドでもある。つまりfind-fileを呼び出しました。でもまずは念のためどこか別の場所にコピーしたいな。その後でファイルのオープンを再開しよう! を実現するコマンドと言える。

それともGUI環境における”右クリックメニュー”に相当する、キーボード駆動型の類似機能と考えるかもしれない。その類比も機能はするが、前者のほうがEmbarkによって実現が可能となった、”遅延バインディング”と怠惰の概念によく合うように思える。

Emacsではまずアクション/動詞(たとえばfind-file)を指定·決定してから、それが作用する対象(たとえばファイル)を選択する。embark-actを呼び出すとこれが逆転する。今度はオブジェクト(ファイル)を決定してから、アクションを自由に選択することができるのだ。

Helmアクションの説明に聞こえる? 分かっている。Embarkはどこでも、すべてのタイプの”オブジェクト”に跨って機能する点が異なる。最初のコマンドでも、wait-I-changed-my-mindコマンドでも機能する点が異なるのだ。事前定義された初期アクションの集合にたいして機能するように構成された、代替えアクションの集合が事前に別途定義されている訳ではない。(あなた自身を含めて)誰も連携させるアクションを前もって予想する必要はない1。この均一かつ一貫したEmacsへの統合により、確認には若干の時間を要するが、両者の間に量ではなく質の違いがもたらされるのである。

これはあるコマンドを開始してミニバッファーで候補を選択、それからembark-actを呼び出して、M-x some-other-commandでかわりそのコマンドを実行できることを意味している。C-x kでバッファーをkillしようとしたが、かわりにそのバッファーに切り替えたくなったのなら、embark-actを呼び出した後にC-x bで切り替えることができる。そのバッファーをちょっと確認したいだけであれば、kill-bufferのプロンプトを残したまま確認することすら可能なのだ!

Embarkはファイル名、バッファー、ブックマーク、URL、テキストリージョン、変数、コマンド、シンボル等、一般的なオブジェクトカテゴリーのほとんどを理解する。

embark-actを呼び出すと、Embarkがオブジェクトカテゴリーそれぞれにたいして、ユーザーが実行したいと思うような一般的なアクションに直接アクセスするためのキーマップのアクティブ化も行う。たとえあなたが実行するのが常にI-changed-my-mindコマンドであっても、このアクションを実行するたびに本来は毎回M-xを使用する必要があるのだが、これを行うことによって不要になる。もちろんわたしが行っているように、このキーマップに独自コマンドを追加することもできる。

わたしはembark-actを文字通り日に数百回使用する。以下にわたしがEmbarkを使用する際の一般的な使い方を示す。これらのうちのいくつかはビルトイン、他のものについては機能させるために若干のelispコードが必要だが、どれも驚くほど役に立つものばかりだ。誤解のないよう言っておくが、このリストはEmbarkがもつ可能性のほんの表面をなぞったに過ぎない。

デモ再現のためのレシピ
わたしはEmacs 27.2でConsultのさまざまなコマンド(consult-grep、consult-locate、consult-dir等)と組み合わせてEmbarkを使用している。これらのデモをあなたのEmacsで正確に再現したければ、以下のパッケージが必要になるだろう: - embark - marginalia - vertico - consult - embark-consult - orderless - consult-dir - ace-window - 0x0 適切なキーで使用したければ、追加で関連コマンド(embark-act、embark-export、consult-*)のバインドが必要になるだろう。

任意のウィンドウを分割して任意のバッファーをオープンする

少し背景の説明が必要だろう。ace-windowはキーボードヒントにもとづくウィンドウ切り替えを提供するパッケージだ。あまり知られていないが、単にウィンドウを切り替える以外の方法でウィンドウにアクションを行う、”ディスパッチメニュー”という機能も提供している。

ace-dispatch-demo.mp4

ビデオ実況
  1. 2つ以上のウィンドウをオープンしてace-windowを呼び出す。
  2. ?を押下してディスパッチメニューを立ち上げる。
  3. ウィンドウを水平方向に分割するためにディスパッチキー(ここではv)を押下する。
  4. 分割したいバッファーに応じたace-windowキー(ここではe)を押下する。
  5. ステップ1と2を繰り返す。
  6. ウィンドウを垂直方向に分割するためにディスパッチキー(ここではs)を押下する。
  7. 分割したいバッファーに応じたace-windowキー(ここではw)を押下する。

ディスパッチキーを使用してウィンドウのkill、移動、分割を行うことができる(?を押すとディスパッチメニューを表示)。

さて、これでEmbark経由でace-window呼び出して、任意の場所(上記ディスパッチメニューを使って作成した分割ウィンドウを含む)にある候補を表示できるようになった。これはわたしがオープンしたバッファー/ファイル/ブックマークを、スクリーン上でわたしが表示したい場所に常に配置できることを意味している。

以下のデモでは(consult-bookmarkで)ブックマーク、(find-fileで)ファイル、(consult-bufferで)バッファーを順にオープンしている。オープンする際には毎回embark-actを実行してからace-windowアクションを選択してace-windowをアクティブにしている。ace-windowで選択を行うことによって、既存のウィンドウのどれにでもバッファーを表示できる。実際のデモでは一歩進んで、ace-windowのディスパッチ機能を使って既存ウィンドウの1つを分割して、関連するバッファーの分割したウィンドウへの表示まで行っている!

embark-ace-open-demo.mp4

ビデオ実況
  1. ファイル、ブックマーク、バッファーの選択を要するコマンド(switch-to-bufferとか)を実行する。
  2. 選択したらembark-actを実行する。
  3. omy/embark-ace-actionを実行する(以下参照)。
  4. バッファーを配置したいウィンドウを選択する。
  5. またはvb(aw-dispatch-alistを参照)で既存のウィンドウを分割してウィンドウを選択、新たに分割されたウィンドウにバッファーを表示する。

これを機能させるにはEmbarkのファイルアクションマップにace-windowの関数をいくつか追加する必要がある:

(eval-when-compile
  (defmacro my/embark-ace-action (fn)
    `(defun ,(intern (concat "my/embark-ace-" (symbol-name fn))) ()
      (interactive)
      (with-demoted-errors "%s"
       (require 'ace-window)
       (let ((aw-dispatch-always t))
        (aw-switch-to-window (aw-select nil))
        (call-interactively (symbol-function ',fn)))))))

(define-key embark-file-map     (kbd "o") (my/embark-ace-action find-file))
(define-key embark-buffer-map   (kbd "o") (my/embark-ace-action switch-to-buffer))
(define-key embark-bookmark-map (kbd "o") (my/embark-ace-action bookmark-jump))

わたしはさらにウィンドウを垂直または水平に分割してバッファーをオープンするアクションも追加しているが、ace-windowのディスパッチメニューで同等以上のことができるので、あなたには多分必要ないだろう!

(eval-when-compile
  (defmacro my/embark-split-action (fn split-type)
    `(defun ,(intern (concat "my/embark-"
                      (symbol-name fn)
                      "-"
                      (car (last  (split-string
                                   (symbol-name split-type) "-"))))) ()
       (interactive)
       (funcall #',split-type)
       (call-interactively #',fn))))

(define-key embark-file-map     (kbd "2") (my/embark-split-action find-file split-window-below))
(define-key embark-buffer-map   (kbd "2") (my/embark-split-action switch-to-buffer split-window-below))
(define-key embark-bookmark-map (kbd "2") (my/embark-split-action bookmark-jump split-window-below))

(define-key embark-file-map     (kbd "3") (my/embark-split-action find-file split-window-right))
(define-key embark-buffer-map   (kbd "3") (my/embark-split-action switch-to-buffer split-window-right))
(define-key embark-bookmark-map (kbd "3") (my/embark-split-action bookmark-jump split-window-right))

ファイルのオープン時にリモートへコピーする

embark-copy-remote-demo.mp4

ビデオ実況
  1. ファイルの選択を要する任意のコマンド(find-file-other-windowとか)を実行する。
  2. ファイルを選択してembark-actを実行する。
  3. ccopy-fileアクションを実行する。Embarkにはこれ用のキーがあるが、ここでM-x copy-fileの実行も可。
  4. 目的となるパスへ移動する。ビデオではconsult-dirを使って、わたしのブックマークからリモートにあるパスに一時的に切り替えている。
  5. RETを押下してファイルをコピーする。コピー先での名前を入力できる。

ここでは何が起きているのだろうか? ファイルの入力を求める任意のプロンプトでembark-actを実行することで、(M-x copy-fileを呼び出したかのように)かわりにファイルをコピーするアクションを選択できるのだ。デモではコピー先の入力を求めるプロンプトにたいして、consult-dirを使用してわたしのサーバーを指すブックマークを挿入、ファイルのコピーはTrampが行っている。

find-fileプロンプトを残したまま、これを行うことすらできるのだ! embark-act呼び出しでプレフィックス引数を指定することにより、プロンプトをそのまま残すことができる。

embark-copy-remote-persist-demo.mp4

ビデオ実況
  1. ファイルの選択を要する任意のコマンド(find-file-other-windowとか)を実行する。
  2. ファイルを選択してプレフィックス引数とともにembark-actを実行する。つまりC-.にembark-actをバインドしている場合にはC-u C-.。
  3. cでcopy-fileアクションを実行する。Embarkにはこれを行うキーが用意されているがM-x copy-fileでも可。
  4. 目的のパスへ移動する。ビデオではconsult-dirを使って、わたしのブックマークからリモートにあるパスに一時的に切り替えている。
  5. RETを押下してファイルをコピーする。コピー先での名前を入力できる。
  6. 操作の前に行っていたfind-file-other-windowプロンプトへの入力を継続する。

最後はfind-fileプロンプトを手作業で閉じてから、ちゃんとファイルがコピーされたかリモートディレクトリーをチェックしている。

バッファーへのミニバッファー候補の挿入

シンプルだけど非常に優秀。

embark-insert-demo.mp4

ビデオ実況
1. ミニバッファーを使って何かの選択を要求する任意のコマンドを実行する。選択は何でもよいが、何かテキストを表示するもの。 2. ビデオではconsult-dirで離れたディレクトリーを選び、そのディレクトリーのファイルを選択している。 3. embark-actを実行する。 4. iを押下してメインとなるバッファーに選択したテキストを挿入する。ビデオではIで選択したファイルの相対パスを挿入した。選択したオブジェクトのタイプに応じてIは違うことを行う。たとえば候補がバッファーなら、バッファーのコンテンツを挿入する。

セッションを継続しつつミニバッファーのファイル候補のシェルコマンドを実行する

でもまず必要なのは… の完璧な例

embark-shell-cmd-demo.mp4

ビデオ実況
  1. ファイルの選択を要求する任意のコマンド(find-fileとか)を実行する。
  2. consult-dirで離れたディレクトリーに切り替える。
  3. ファイルを選択して、プレフィックス引数とともにembark-actを実行する。つまりC-.embark-actをバインドしている場合にはC-u C-.
  4. &を押下してasync-shell-commandアクションを実行する。Embarkのキーマップにはこれを行うためのキーがあるがM-x async-shell-command、あるいはasync-shell-commandのデフォルトのキーバインディング(M-&)でも実行可。
  5. プロンプトにコマンドを入力する。ファイル名はすでに用意されている。ファイルについてさらに情報を得るために、シェルコマンドfileを使用した。
  6. RETを押下してコマンドを実行して、find-fileの入力プロンプトにリターンする。

ファイルについてさらに情報を得るために、find-fileのプロンプトを終了させずにシェルコマンドの”file”を呼び出した。

セッションを維持したままrootでファイルをオープン

コマンドの前のsudo忘れちゃったの、のEmacs版である。シェルならプロンプトの先頭に戻ってタイプして追加、あるいはsudo !!を召喚して儀式を執り行うところだが、EmacsではEmbarkアクションを使用する。

embark-sudo-demo.mp4

ビデオ実況
  1. ファイルの選択を要求する任意のコマンドを実行する。わたしはファイルシステム上でroot所有のファイルをlocateするためにconsult-locateを使用した。
  2. ファイルを選択してプレフィックス引数とともにembark-actを実行する。つまりC-.embark-actをバインドしている場合にはC-u C-.
  3. Ssudo-findアクションを選択した。注意:このアクションをキーマップに追加する必要があるだろう(以下参照)。かわりにM-x sudo-find-fileやそれのグローバルバインディングの実行でも可。

わたしはconsult-locateをコマンドを実行したが、前の例と同じようにファイルの入力を求めるコマンドなら何でも機能するはずだ。わたしはinitファイルからもはや来歴不明なスニペットを流用したが、sudoプログラム向けのsudo-editパッケージもある。

(defun sudo-find-file (file)
  "Open FILE as root."
  (interactive "FOpen file as root: ")
  (when (file-writable-p file)
    (user-error "File is user writeable, aborting sudo"))
  (find-file (if (file-remote-p file)
                 (concat "/" (file-remote-p file 'method) ":"
                         (file-remote-p file 'user) "@" (file-remote-p file 'host)
                         "|sudo:root@"
                         (file-remote-p file 'host) ":" (file-remote-p file 'localname))
               (concat "/sudo:root@localhost:" file))))

Embarkアクションとしてsudo-find-fileを使用するには、embark-act呼び出し後にM-xやグローバルバインディングから実行するか、あるいはEmbarkのファイルアクションのマップに追加してさらに短縮することもできる:

(define-key embark-file-map (kbd "S") 'sudo-find-file)

テキストリージョンを0x0にアップロード

embark-0x0-region-demo.mp4

ビデオ実況
1. バッファーでテキストのリージョンを選択する。 2. embark-actを実行する。 3. U私押下して0x0-dwimアクションを選択する。注意: このアクションをキーマップに追加する必要があるだろう(以下参照)。 4. リージョンのテキストが0x0にアップロードされて、URLがkill-ringに追加される(ビデオの最後のメッセージに注目)。

0x0-dwim関数のために0x0パッケージを使用している。URLにたいしてEmbarkアクションとして呼び出すとURLを短縮、ファイルの場合にはファイルをアップロードする。最後の(0x0-dwimからの)エコーエリアのメッセージによって、アップロードURLがkillリングにコピーされたことがわかる。他の例と同じように、embark-actの後に0x0-dwimを呼び出したり、Embarkのキーマップの1つとしてより短いキーを定義してもよい:

(define-key embark-region-map (kbd "U") '0x0-dwim)

ミニバッファーからパッケージのURLをvisit

embark-package-url-demo.mp4

ビデオ実況
1. パッケージの選択を要する任意のコマンド(describe-packageとか; デフォルトではC-h P)を実行する。 2. パッケージを選択してembark-actを実行する。 3. uを押下してembark-browse-package-urlアクションを実行する。

今回は “実は…URLを頼む” の前にdescribe-packageコマンドを実行したが、describe-packageでも特別な扱いは不要であり、他の例と同様である。ミニバッファーでパッケージのリストを表示するすべてのコマンドは、同じ一連のEmbarkアクションを提供する。

バッファー内の任意の場所から変数をセット

素早く変数をセットする。コードのテスト時には非常に役に立つ。

embark-set-var-demo.mp4

ビデオ実況
1. バッファー内の変数にポイントを移動する(describe-variableのようにミニバッファーで変数の選択を要求するコマンドでも可)。 2. embark-actを実行する。 3. =を押下してset-variableアクションを実行する。Embarkはこれを行うためのキーをキーマップに所有するが、M-x set-variableの呼び出しでも可。 4. 変数に新しい値をセットする。

このデモではEmbark自身の変数キーマップにset-variable用のエントリーを所有している(=にバインド)が、単にM-x set-variableで呼び出しでも可。

任意の場所からコマンド名にキーバインディングを追加

すべてのキーをセットする。

embark-set-key-demo.mp4

ビデオ実況
1. バッファー内でコマンド名が表示されている場所にポイントを移動する(describe-commandのようなミニバッファーでコマンドの選択を要求するコマンドを実行してもよい)。 2. embark-actを実行する。 3. gを押下してglobal-set-keyアクションを実行する。Embarkのキーマップにはこれを行うキーが定義されているが、M-x global-set-keyを呼び出してもよい。 4. コマンドに新たなキーバインディングをセットする。

Embarkは自身のキーマップでglobal-set-keyを実行するアクションを提供しているが、コマンド名にポイントを配置してembark-actを実行した後にM-x global-set-keyを呼び出してもよい。embarkのキーマップにはlocal-set-keyもセットされている。

embark-export: 要点が知りたいのでリストください

すべてがEmbarkだったなら、わたしは幸せだったこちだろう。しかしembark-actさえもっとも優れた機能ではないのだ。構成を可能にするということにかけては、embark-export(と劣化版のembark-collect)こそがEmbarkの至宝と言えるだろう。これらのコマンドはミニバッファーの候補リストから永続的なコレクションを作成する。一方はivy-occur、もう一方はEmacsよりも巧妙にEmacsライブラリー同士を結びつける接着剤の役割りを果たすのだ。理由は例を使って説明しよう:

Emacsのパッケージ候補をパッケージメニューにエクスポート

package-menu-modeでEmacsのシェル関連のすべてのパッケージを見たい? それならembark-exportの守備範囲だ:

embark-package-export-demo.mp4

ビデオ実況
1. 何かパッケージの選択を要求するコマンド(describe-packageとか; デフォルトではC-h Pにバインドされている)を実行する。 2. (オプション)何かテキストを入力して補完リストをフィルタリングする。 3. embark-exportを実行する。

embark-exportの背後には、可能なら常にEmacsのビルトイン機能を再利用するという優れたアイデアが存在する。すでにpackage-menuライブラリーがパッケージの表示を処理しているので、それを再利用するのだ。ユーザーが指定した条件でパッケージのリストを生成しているのに、車輪を再発明する必要があるだろうか?

“imenu-list”候補の収集

embark-collectは(ユーザーの入力によってフィルタリングされた)ミニバッファーの補完候補にたいする永続的なコレクション作成する。これにより、基本的に何かを”リスト”するようなすべてのパッケージは、わたしにとって時代遅れのものとなってしまった。以下の例ではファイルに何かを行う際に使用できるimenuアイテムのフィルター済みリストを作成している:

embark-imenu-list-demo.mp4

ビデオ実況
1. 何かファイルをvisitする際にimenuやその別バージョン(わたしの場合はconsult-imenu-all)を実行する。 2. (オプション)何かテキストを入力して補完リストをフィルタリングする。 3. embark-exportを実行する(実際のところ水面下ではembark-collectを実行している; embark-collectを直接実行してもよいが使うコマンドは1つだけのほうがシンプルだろう)。 4. 永続的なコレクションバッファーでRETを押下すれば、ファイル内のそのシンボルに移動する。

デモでは示さなかったが、Collectionバッファーではすべてのembark-actアクションが利用できる。embark-collect-direct-action-minor-modeをチューニングすれば、(最初にembark-actを呼び出さなくとも)直接呼び出すことさえ可能だ。

dired-bufferへのファイル候補のエクスポート

入手までに紆余曲折を経た、保管しておきたいファイルリストをお持ちでは? ファイルのリストを作成するために作られたdired、作成されたファイルリストへの配慮はembark-exportに任せよう:

embark-file-export-demo.mp4

ビデオ実況
1. 何かファイルの選択を要求するコマンドを実行する。わたしはディレクトリー配下で、あるパターンにマッチするすべてのファイルを検索するためにconsult-fdを使用した。 2. (オプション)何かテキストを入力して補完リストをフィルタリングする。consult-fdでは検索を行う文字列を#で終了してから、その結果をフィルタリングするために用いる文字列を開始する。 3. embark-exportを実行する。

“リスティング”に基づいたこれとは別の機能であるfind-name-diredも過去のものとなった。

ibufferへのバッファー候補のエクスポート

バッファーのリストなら何でもibufferにエクスポートできる(そう来ると思った、と言われそう)。

embark-buffer-export-demo.mp4

ビデオ実況
1. 何かバッファーの選択を要求するコマンドを実行する。わたしが使ったのはconsult-buffer。 2. 何かテキストを入力して補完リストをフィルタリングする: - consult-bufferのリストをバッファーに絞り込むためにb SPCを押下して、 - \*で始まるすべてのバッファー(つまり"スペシャル"バッファー)を除外するために!と*をタイプする[^2]。 3. embark-exportを実行する。

これは変数の検索から、必要に応じて関連アイテムへの本格的なaproposへと遷移するための非常に優れた手段として使用できる。

grepバッファーへのgrepや行の候補のエクスポート

occurのような結果(consult-lineやgrep、xrefなどの出力)であればgrepバッファーにエクスポートできる。

embark-grep-demo.mp4

ビデオ実況
1. grepスタイルのマッチリストを生成するコマンドを実行する。デモでわたしが使用したのはconsult-ripgrep。Consultベースの他のオプションとしてはconsult-grep、consult-git-grep、consult-lineがある。 2. テキストをタイプしてgrep結果を検索する。 3. (必須ではない)わたしはvertico-next-groupとvertico-previous-groupでマッチしたファイルを散策した。 4. embark-exportを実行する。 5. C-c C-fでgrepバッファーでnext-error-follow-minor-modeをオンに切り替える。これにより… 6. grepバッファーで M-nとM-p(compilation-next-errorおよびcompilation-previous-error), でマッチ間を移動して、}と{でマッチした次/前のファイルに移動する。

これは通常のgrepバッファーであることに注意してほしい。つまりgrepバッファーを編集して変更のすべてをファイルに書き戻す、wgrepのようなあなたのいつものテクニックのすべてが使用可能なのだ。

ボーナス: EmbarkのアクションをHelmのように使う

これまでの例では利用可能なembarkアクションは、そのフレームのどこかのウィンドウに表示されていた。Embarkにはプリセットされたアクションをリストする”プロンプター(prompter)”が複数あり、少し手間をかければHelmと似たような何かをセットアップできる2:

embark-helm-demo.mp4

ビデオ実況
1. 何かミニバッファーでの選択を含むようなコマンドを実行する(ビデオではconsult-buffer)。 2. (オプション)何かタイプして補完リストをフィルターするか、バッファーを選択する。 3. TABを押下してembarkアクションのリストに切り替える。 4. TABをもう一度押下して候補リストに戻る。 5. アクションリストをフィルターするために、名前でアクションを検索する(ビデオでは"kill")。 6. 消して別のアクションを検索(今度は"diff")して、diff-buffer-with-file actionを選択する。 7. RETを押下して、選択されたバッファーでdiff-buffer-with-fileを実行する。 8. (オプション)diff-hunk-nextとdiff-hunk-prevでdiffのhunk間を移動する。 9. (オプション)outline-cycleでdiff選択を折りたたむ。 10. もう一度consult-bufferを実行して、バッファーを選択する。 11. TABでもう一度アクションリストを切り替える。 12. @を押下して、名前ではなくキーバインディングでアクションを呼び出す。 13. kでバッファーをkillするembarkアクションを呼び出して、選択されたバッファーをkillする。

デモではC-<tab>で、(helmのように)アクションリストや補完リスト間を行ったり来たり切り替えている。アクションリストではアクションをタイプするか(マッチにはcompleting-readを使用)、@を前置してキーバインディングで直接アクションを呼び出すことができる。

手間の部分:

(defun with-minibuffer-keymap (keymap)
  (lambda (fn &rest args)
    (minibuffer-with-setup-hook
        (lambda ()
          (use-local-map
           (make-composed-keymap keymap (current-local-map))))
      (apply fn args))))

(defvar embark-completing-read-prompter-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "<tab>") 'abort-recursive-edit)
    map))

(advice-add 'embark-completing-read-prompter :around
            (with-minibuffer-keymap embark-completing-read-prompter-map))
(define-key vertico-map (kbd "<tab>") 'embark-act-with-completing-read)

(defun embark-act-with-completing-read (&optional arg)
  (interactive "P")
  (let* ((embark-prompter 'embark-completing-read-prompter)
         (embark-indicators '(embark-minimal-indicator)))
    (embark-act arg)))

上記例のvertico-mapについては、お好みの補完システムのミニバッファー用のアクティブキーマップに置き換えてほしい。デフォルトはminibuffer-local-completion-mapだ。

Embark使えばアクションに制限がないことを忘れないでほしい。Helmとは違うのだ! embark-actを実行した後にキーバインディングやM-xで必要に応じてコマンドそのものを呼び出すことができるのだ。

33%

これが”Embarkの例のやつ”と評される便利な15の機能だが、embark-becomeについてはまだ触れていない。embark-prefix-help-map、embark-which-key-prompter、Embarkのターゲットやターゲット巡回、他にも半ダースに及ぶEmbarkの気の利いた機能や魅力についても同様。でもそれは次の機会にでも。

かわりといってはなんだが、デモで使用した主なパッケージに触れて結びとさせてもらおう。

  • Omar Antolin Camarenaのembark。彼と交流するのは楽しかったし、機能にたいするわたしのリクエストにもこころよく応えてくれた。embarkキーマップへのアクション追加やEmbarkをカスタマイズするなら、READMEはとても読みやすく非常に有益なので、熟読することをお勧めする。
  • Emacsビルトインにたいするさまざまな機能強化についてはこちらをconsult(参照)してほしい。ファイルのオープンにはconsult-locateconsult-find(実際はconsult-fd)、グルーピングつきのカラフルなimenuにはconsult-imenu、ディレクトリーを横断するgrepにはconsult-ripgrepがある。

ミニバッファーの注釈にはmarginaliaを使用した。Omar AntolinとDaniel Mendlerにより共同で保守されている。

  • ミニバッファーの補完インターフェイスとしてはverticoを使用した Consult、Vertico、MarginaliaはすべてDaniel Mendlerによって開発された。リストには含めなかったCorfu以外にもこれだけのパッケージだ、彼はきっと眠らないに違いない。
  • 補完スタイルはorderlessを使用した。これも開発者はOmar Antolinで、テキストをミニバッファーの補完候補と個別にマッチするために使用している。これら5つのパッケージを組み合わせることによって形成されるMOVECペンタグラム(訳注:MOVECはMarginalia、Orderless、Vertico、、Embark、Consultの頭文字、ペンタグラムは五芒星で魔術的な意味をもつ5つの頂点をもつ星印)は、緩やかにバインドされたEmacsパッケージ全体を、現代的で密に結合するための構成可能な拡張パッケージコレクションである。
  • 素早いディレクトリー移動にはconsult-dirを使用した。上述した例において、ミニバッファーの中から遠くのディレクトリーに移動する際に何度か使用している。
  • popperはembark-collectやヘルプ、その他の一時的なバッファーがスクリーン上に表示される際に使用している(訳注: 開発は著者さん)。
  • abo-aboが開発したace-windowを使用している。Ace-WindowとAvyのdispatch-keyのアイデアについては、速攻でpopperにパクらせていただいた。わたしの理解が正しければ、彼のIvy-Occurは初期のEmbark-Collectにも影響を与えているはずだ。
  • William Vaughnの0x0は、自分で思った以上に頻繁に使用している。

最後にDoom Emacsユーザー向け注意事項を簡潔に記す: Doomには素の状態でEmbarkが同梱されている(2019年9月現在)ので、embark-actとembark-collectのキーを調べる以外何も行う必要はない。

これらの例が示唆する事実にも関わらず、おそらくわたしはEmbarkの能力の3分の1も使用できていないのだろう。たとえそうであったとしても、いつでもアクションの変更したり連鎖させられるので、経験と勘によるEmacsの操縦を可能にしてくれるのだ。2つ目の予期せぬ利点として、わたしが決して使うことがなかったであろうコマンドやリストを、抵抗なく利用できるようになることだ。transpose-regionsやapply-macro-to-region-linesのようなコマンド、あるいはdired、ibuffer、package-menuのような機能を使わなければアクセスできなかったカスタマイズされたリストをスムーズに利用できるようになった3。このようなバッファーを素早く作成できるので、diredやibufferを使う方法を知ることによって、数倍の利益が得られるようになる。これらのような機能とミニバッファーとの対話やテキストリージョンをシームレスに構成する際に、Emacsに組み込まれた無数のコマンドやライブラリーのパワーを増幅するレバーとして振る舞うのがEmbarkなのである。

脚注

  1. もちろんHelmとEmbarkはどちらも自身の役割を良好にこなすよう事前に調整されている訳だが。 

  2. その通り。これは完全なHelmスタイルではない。なぜなら候補/アクションの表示にバッファーではなくミニバッファーを使うからだ。そこが躊躇する原因ならばvertico-bufferを使えばよい。 

  3. カスタマイズされたpackage-menuリストへのアクセスは技術的には可能である。完全なパッケージリスト(M-x list-packages)から、/ nでパッケージ名にたいしてregexpでフィルタリングできる。