つーさにブログ

つうさにのメモ用ブログ

Emacsのivy.elとquelpa.elでのニッチな不具合について(quelpa-melpa-recipe-stores)

Emacsを便利にしたい!という理由で様々なパッケージを入れています。

しかし、インストールするパッケージのelispソースコードをほとんど見ていない私はまた首を傾げる事態に遭遇してしまいました。とほほ

環境 : Emacs 25.3.1

Ivy

IvyEmacsにおける補完機能(completion)用パッケージです。Emacsで補完といえばHelmが有名ですが、こちらはよりシンプルでminibuffer内に候補が表示されます。

M-x ivy-modeなどでグローバルマイナーモードであるivy-modeを有効にすると、completing-read-functionivy-completing-readが設定されます。これによって、minibufferでの補完にivyの補完が用いられます。つまり、標準の入力補完completing-read-default(minibufferでTABを押すと*Completions*バッファが出てくるやつ)とはおさらばということです。やったね。

ちなみに、このivy-modeによるcompleting-read-functionの変更だけでも便利ですが、同レポジトリにあるcounsel.elやswiper.elには、Emacsのデフォルトの関数のラッパー(counsel-modeで有効化)や便利な関数があるので利用するとさらに便利です。counsel-M-xは(インストールされていれば)smexamxに対応しているので嬉しいですね。MELPAからpackage.elでインストールする際は、counselをインストールすれば依存でswiperとivyもインストールしてくれます。

quelpa

quelpaEmacsにおけるパッケージの管理ツールです。標準のpackage.elのラッパーとして働き、またquelpa-use-packageuse-packageと統合できるため便利です。(最近githubからframagitにレポジトリを移したようです)

ただし、quelpaは基本的にMELPAのみを対象としているため、パッケージをインストールする際、依存パッケージがMELPAに登録されていない場合エラーとなります。例えば、magitはlet-alist-1.0.5を必要とするが、Emacs25.3.1で標準に入っているものはlet-alist-1.0.4であり、また、MELPAのレシピにはないためインストールに失敗します。

追記 2018-08-29
インストールに失敗しますと書きましたが、これは自分が(setq package-archives nil)としていたためです。package-archivesをいじらなければ、普通にインストールできました。つまり、以下は(setq package-archives nil)としている際に起こる不具合の説明です。相当ニッチになってしまいました。
追記終わり

エラーを回避するにはquelpa-melpa-recipe-stores(もともとMELPAのレシピのディレクトリのみ)に必要なレシピファイルを置いたディレクトリを追加すればよい。そうすればquelpaはレシピを見てパッケージを取得できます。もしくは、.emacsの中で完結させたい場合は

(add-to-list
 'quelpa-melpa-recipe-stores
 '((let-alist :fetcher url
          :url "http://git.savannah.gnu.org/cgit/emacs.git/plain/lisp/emacs-lisp/let-alist.el"
          :version original)))

を追加すればよい。(これはquelpaの作者のconfig fileを参考にしました)

これでmagitパッケージをインストールできました。ちゃんちゃん。と言いたいところですが、この方法だととある不具合が生じます。M-x quelpaでエラーを吐かれてしまい使用できません。

追記 2019-01-26
現在最新版のivyでは以下のエラーは起こりません。いつの間にか直ったようです。
追記終わり

不具合の原因、解決策

quelpaだけの不具合ならquelpaだけ紹介すればいいのにIvyを先に紹介している。まあつまり原因はIvyにありました。(弱い人なので原因究明にとにかく時間がかかりました...)

実は、ivy-completing-readcompleting-read-defaultでは仕様が少し違いました。

手っ取り早くサンプルを示します。*scratch*バッファなどで試せます。(上からそれぞれの末尾でC-j)

(ivy-mode -1) ; ivy-modeを無効化
(completing-read-default "select: " '("a" "b" (c C))) ; => TABで3つの候補が出る
(ivy-completing-read "select: " '("a" "b" (c C))) ; => error

上のようにcompleting-read-defaultでは第2引数(collection)にリストを与え、その要素がリストの場合でもエラーとならず、そのリストのcarが候補として補完されます。しかし、ivy-completing-readでは補完を作れずエラーとなってしまいます。

おそらくivy-completing-readではcollectionに与えるリストの要素にリストと文字列が混在する使用法を想定していません。edebugを使ってみましたが、condを使っての場合分け、もしくはsortの関数が問題のようでした。

解決方法としてはivy-completing-read-handlers-alistに、補完にivy-completing-readを用いないようにしたい関数と別の補完関数のconsを追加することです。

  (add-to-list 'ivy-completing-read-handlers-alist '(quelpa . completing-read-default))
  (add-to-list 'ivy-completing-read-handlers-alist '(quelpa-expand-recipe . completing-read-default))

こうすればivy-modeが有効でもM-x quelpaM-x quelpa-expand-recipeは補完にcompleting-read-defaultが使われ、エラーは起こりません。しかし、Ivyの縦に並ぶ補完はとても便利であるため、使えないのは悲しいですね。

まとめ

実を言うとM-x quelpaを実行する機会はないです。インストールしたいなら.emacsに記述して評価する(eval)し、またM-x quelpaだとquelpa-melpa-recipe-storesにあるレシピしか選択できません。C-u M-x quelpaとするとアップデートされるという機能がありますが、M-x quelpa-upgradeならMelpaのレシピに限られず、quelpaでインストールしたパッケージ全てをアップデートしてくれます。

追記 2018-08-29
嘘です。C-u M-x quelpaが便利なので、使えた方がいいと思います。また、el-getを知ったら、quelpa-melpa-recipe-storesにリストを追加するのではなく、ディレクトリを追加してそこに自分でrecipeファイルを書いて置いておくのもいいのかもしれないと思いました。それと、M-x quelpa-upgradeはあまりオススメしません。C-u M-x quelpaで一つずつアップデートするのがいいと思います。
追記終わり

結局は、どうしてM-x quelpaでエラーが発生するんだろう?という疑問を解決できた。ので嬉しい。というお話でした。

ivyとquelpaを使用しているかつquelpa-melpa-recipe-storesディレクトリではなくリストを追加しているユーザに対する記事でした。とてもニッチですね。

Ivyの改良について

githubのレポジトリなんだしIssueを送ったらどうかと一度思いましたが、そうなったら多分自分でコードを書いてプルリクエストしてねと言われそうなので弱い私は上の方法で満足しました。ivy.el読んだけど直すのは結構面倒くさそうです。

追記 2019-01-26
久々に試したら直っててびっくりしました。
上にあげたエラーが出ていたコード例も現在は普通にivyの補完が出てきます。
直してくださった方に感謝。 追記終わり