- 生成AIのチャット欄が重いとき、Emacsで下書きしてから貼り付けています。そのとき company の自動補完が日本語入力に割り込んできます。
company-global-modesで除外できるのはメジャーモードの単位です。*scratch*を外そうとするとlisp-interaction-modeのバッファがすべて巻き添えになります。- バッファ名で判定するには、
company-mode-hookの中から自分を無効化する方法があります。 - そもそも下書き用のバッファを別に作るほうが、
*scratch*を Lisp の実験場として残せます。

(defun my-disable-company-in-scratch ()
(when (and company-mode
(string= (buffer-name) "*scratch*"))
(company-mode -1)))
(add-hook 'company-mode-hook #'my-disable-company-in-scratch)
Code language: Lisp (lisp)
1. 補完が助けになるときと、雑音になるとき
生成AIのチャット欄に長い文章を打ち込んでいると、入力が引っかかることがあります。 一文字打つたびに待たされる感じ。 それが嫌なときは、 Emacs を開いてそこで書き、書き終わってからコピーして貼り付けています。
副産物として、途中で Enter を押してしまう事故もなくなりました。 半分だけ書いた質問が送信されて、それを前提に返事が返ってくる、というのを何度かやらかしています。
ただ、Emacs 側で別の邪魔が出てきました。 company(complete anything:何でも補完する)の候補がひょこひょこ出てくるのです1。 コードを書いているときは、関数名を三文字打てば残りが出てくるので助かります。 文章を書いているときは、変換候補でもないものが視界の端で動くだけ。
2. company-global-modes はメジャーモード単位でしか効かない
2.1. 除外リストの書き方
global-company-mode は、バッファを開くたびに「このバッファで company を有効にするか」を判定します。 その判定に使われるのが company-global-modes という変数です。
指定できる値は三通りあります。 t ならすべてのバッファで有効、nil ならどこでも無効、
リストを渡すとそのリストに含まれるモードだけで有効になります2。
また、先頭に not を置くと意味が反転して、除外リストとして働きます3。
(setq company-global-modes '(not lisp-interaction-mode))
Code language: Lisp (lisp)
これで *scratch* の補完は止まります。
2.2. *scratch* は lisp-interaction-mode
止まるのですが、止まりすぎます。
リストの要素はメジャーモードの名前です。 バッファ名ではありません。 *scratch* のメジャーモードは lisp-interaction-mode なので、この設定を書くと、自分で M-x lisp-interaction-mode して作った別の実験用バッファでも補完が効かなくなります4。
マイナーモードの状態自体はバッファごとに持っています5。 あるバッファで company が有効で、隣のバッファでは無効、という状態は成立します。 バッファ単位で保持しているのに、有効にするかどうかの判断だけがメジャーモード単位。 このずれが、*scratch* だけを狙い撃ちできない理由です。
3. バッファ名で判定してフックから切る
3.1. company-mode-hook で自分を無効化する
判定の入口が使えないなら、スクラッチバッファに切り替えた直後に無効にする処理を追加します。
(defun my-disable-company-in-scratch ()
(when (and company-mode
(string= (buffer-name) "*scratch*"))
(company-mode -1)))
(add-hook 'company-mode-hook #'my-disable-company-in-scratch)
Code language: Lisp (lisp)
company-mode-hook は、company が有効になったときだけでなく、無効になったときにも走ります6。 (company-mode -1) を呼べばフックがもう一度呼ばれるわけです。 そのときは company-mode が nil になっているので、when の条件が偽になって、そこで止まります。 条件の先頭に company-mode を置いているのは、この往復を一周で終わらせるためです。
バッファ名を直接見ているので、同じ lisp-interaction-mode でも *scratch* 以外は影響を受けません。
3.2. 設定を読み込んだ時点の scratch には効かない
一つ落とし穴があります。
*scratch* は Emacs の起動時に作られるので、設定ファイルを評価し直しただけでは、すでに開いている *scratch* の company は有効なまま残ります。 フックは、これから company が切り替わるバッファにしか作用しません。 その場で反映させたいなら、*scratch* で M-x company-mode を一度実行します7。 次回の起動からは、何もしなくても無効の状態で立ち上がります。
4. 下書き用のバッファを別に用意する
ここまで書いておいて何ですが、*scratch* を下書き帳として使うこと自体を見直す手もあります。
*scratch* が lisp-interaction-mode なのは、S式を書いて C-j でその場で評価するためです。 日本語の文章を書く場所としては、たまたま最初から開いているという理由で選ばれているだけ。 initial-major-mode を text-mode にすれば、*scratch* は最初から文章向きのバッファになりますし、lisp-interaction-mode ではなくなるので company の除外設定も素直に書けます8。
(setq initial-major-mode 'text-mode)
Code language: Lisp (lisp)
Lisp の実験場も残したいなら、下書き用のバッファを別に作って text-mode にしておくほうが分けやすいです9。
Emacs の設定は、こういう「機能そのものは正しいのに、自分の使い方とだけ噛み合わない」ところを削っていく作業が多い気がします。 補完を切りたいだけの話が、マイナーモードとメジャーモードの関係を調べる話になりました。 遠回りですが、こういう遠回りのほうが記憶に残ります。
- Companyは、Emacsのバッファ内で動くモジュール式の補完フレームワークとして公開されています。 – company-mode for Emacs
- company.el内の説明では、リストを渡す場合の要素はmajor-modeのシンボル名だと明記されています。 – company-mode/company.el at master
- 公式マニュアルは、notを先頭に置いてerc-modeやeshell-modeを外す指定例を挙げています。 – Customization (Company User Manual)
- Emacsマニュアルは、起動時に存在するscratchがEmacs Lisp式を対話的に評価するためのバッファで、そのメジャーモードがLisp Interaction modeだと説明しています。 – Lisp Interaction (GNU Emacs Manual)
- マイナーモードには、バッファごとに有効・無効を切り替えられるものと、セッション全体に効くグローバルなものがあります。 – Minor Modes (GNU Emacs Manual)
- define-minor-modeが作るトグルコマンドは、モード変数を設定して本体を実行したあとにmode-hookを走らせます。マニュアルは、グローバルに有効にしたマイナーモードを特定のモードで無効にする手段として、そのモードのフックを使うことを挙げています。 – Defining Minor Modes (GNU Emacs Lisp Reference Manual)
- Companyのユーザーマニュアルは、セッションをまたいで常時有効にするなら設定ファイルに(global-company-mode)を書くよう案内しています。バッファ単位の切り替えはM-x company-modeで行います。 – Company User Manual
- initial-major-modeは最初のscratchバッファのメジャーモードを決めるユーザーオプションで、既定値はlisp-interaction-modeです。バッファ名がscratchのときにだけ、この変数が参照されます。 – Auto Major Mode (GNU Emacs Lisp Reference Manual)
- initial-major-modeを変えるなら、initial-scratch-messageに入っているLispコメント形式の初期メッセージも合わせて書き換えるとよいと紹介されています。 – Configure the Scratch Buffer’s Mode | Emacs Redux