1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Devil Modeマニュアル

Last updated at Posted at 2025-01-15

TOC

Devilモード

Devilモードはカンマキーと引き換えに、Emacsでの修飾キーなしの編集経験を提供するモードだ。そう、カンマキー(comma key)だ! これは普通ならテキストのほぼすべての隅に句読点を挿入するためのキーである。そう、このモードは少しへそ曲がりなモードと言えよう! でなければ悪魔(Devil)などと呼ばれないのではないだろうか? もっと理に適ったモードであればもっと神聖な、そうGod(神)モードとでも呼ぶかもしれない。しかし悲しいかな、このモードには神聖な何かなどは存在しない。かわりにと言っては何だが、悪魔のテリトリーにようこそ! ときには句読点を入力するためにカンマキーを使用する権利が与えられることもあるが、それは悪魔を誘惑できた場合に限られる。この邪悪なドメインにおいてはカンマキーを諦めること、そしてよこしまな秘密を指先に唱えるような編集体験を受容しなければならないことに注意して欲しい!

1. イントロダクション

Devilモードは設定可能な変換ルールにしたがって、横取りしたキーストロークをEmacsのキーシーケンスに変換する。たとえばデフォルトの変換ルールでは, x , fとタイプすると、DevilモードがそれをC-x C-fに変換する。カンマキー(,)を修飾キーのcontrol(C-)とみなすのはとんでもない選択に思えるかもしれない。結局のところカンマキーは散文とコードの両方において、非常に重要な句読点文字なのだ。本当に,を修飾キーC-として使っていくことができるのだろうか? この恐ろしいアイデアだが、実際にはそれほど手間を掛けずに機能させられることが判ったのだ。少なくともわたしの場合は問題なかったので、あなたも問題なく使うことができるだろう。もしカンマキーの使用に問題があっても、Devilでは別のキーを修飾キーC-に設定することもできる。カスタムDevilキーとそれに続くセクションに、いくつか例を挙げてあるので参考にして欲しい。

疑り深い読者であれば当然こう尋ねるだろう: ,C-に変換されるとしたら、一体どうやってリテラルの,をテキストに挿入できるのか? その答えについてはセクションカンマのタイプを参照して欲しい。しかしその前に基本事項をいくつか補足しておく必要がある。本腰を入れて解明していこう。多分気に入る筈だ。もしお気に召さなければGodモードやEvilモード、素のキーバインディング、あるいは何か他の面白いことに戻るのは何時でもできるのだから。

2. 表記

ドキュメント内で使用する表記に関する簡単な注意: 前に挙げた例では, x , fC-x C-fに変換する例を見てもらった。これはキーストローク,x,fctrl+x ctrl+fに変換されというのが本当の意味だ。実際にはカンマの後のスペースはタイプせず、キー,の後に直接キーxをタイプすることになる。しかしこのドキュメントで使用するキーシーケンスでは、、キーストロークそれぞれの間にスペースを挟んだ表記を使用する。この表記はEmacsにおいてキーシーケンスを表すときやkey-descriptiondescribe-keyといったEmacs関数がキーシーケンスを表す際に一般的に用いる方法と整合性があるからだ。本当にスペースをタイプする必要がある場合には、SPCと表すことにしよう。

3. インストール

DevilはNonGNU ELPAかMELPAから入手できる。あなたには既にELPA/MELPAからパッケージをインストールする際のお気に入りのやり方があるのかもしれない。その場合にはdevilというパッケージ名で入手したDevilをインストールすればよい。Emacsのバージョンが28.1以降であればデフォルトでNonGNU ELPAが有効になっているので追加ステップなし、M-x package-install RET devil RETとするだけで、Devilを簡単にインストールできるだろう。しかしより万全を期する場合向けに、次のいくつかのサブセクションではDevilをインストールできる基本的な方法をいくつか紹介しよう(具体的なやり方としてはそれぞれまったく異なる方法だ)。

3.1 MELPA経由のインタラクティブなインストール

以下はDevilの最新バージョンをMELPAからインストールするステップだ:

  1. Emacsのinitファイル(~/.emacs~/.emacs.d/init.el、または~/.config/emacs/init.el)に以下を追加:
     (require 'package)
     (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
     (package-initialize)
  1. 変更したinitファイルでEmacsを起動して、以下のコマンドをタイプ:
     M-x package-refresh-contents RET
     M-x package-install RET devil RET
  1. 以下のコマンドでDevilが正常にインストールされたか確認:
     C-h f devil RET
  1. 以下のコマンドでDevilモードを有効化:
     M-x global-devil-mode RET
  1. , x , fとタイプしてDevilがそれをC-x C-fに変換すること、そのキーに応じたコマンドが呼び出されることを確認する

3.2 MELPA経由の自動インストール

以下はDevilのインストールと有効化にElispを使う、基本的だが別のやり方:

  (require 'package)
  (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
  (package-initialize)
  (unless package-archive-contents
    (package-refresh-contents))
  (unless (package-installed-p 'devil)
    (package-install 'devil))
  (global-devil-mode)
  (global-set-key (kbd "C-,") 'global-devil-mode)

, x , fとタイプすればDevilがそれをC-x C-fに変換するし、そのキーに応じたコマンドが呼び出されるだろう。C-,とタイプすればDevilが無効になり、もう一度C-,とタイプすれば有効になる

3.3 Gitソースのインストール

DevilをGitレポジトリから取得したい場合は以下のステップ:

  1. システムにDevilをclone :
     git clone https://github.com/susam/devil.git
  1. Emacsのinitファイルに以下を追加:
     (add-to-list 'load-path "/path/to/devil/")
     (require 'devil)
     (global-devil-mode)
     (global-set-key (kbd "C-,") 'global-devil-mode)
  1. Emacsエディターを起動すればすべてのバッファーでDevilモードが有効になっていて、モードラインにはそれぞれDevilが表示されている筈だ

  2. , x , fとタイプすればDevilがそれをC-x C-fに変換するし、そのキーに応じたコマンドが呼び出されるだろう。C-,とタイプすればDevilが無効になり、もう一度C-,とタイプすれば有効になる

4. Devilを使う

以下にDevilの使い方をデモンストレーションするいくつかの例を示す(Emacsの素のキーバインディングのまま変更されておらず、Devilのカスタマイズもしていないものとする):

  1. , x , fをタイプするとDevilがC-x C-fに変換して、find-fileコマンドを呼び出す

  2. , pとタイプすると1行上に移動

  3. 複数行の移動は, p p p ...。Devilの一部のキーシーケンスは、デフォルトではrepeat(繰り返し)ができる。repeat可能なDevilキーシーケンスの場合には、そのDevilキーシーケンスの最後のキーをタイプして何回でも繰り返すことができる

  4. repeat可能なキーシーケンスは、すべてrepeat可能なキーシーケンスグループに属している。前と同様に, p p pとタイプして、カーソルを3行上に移動してから間を開けずにn nとタイプすれば、カーソルは2行下に移動する。さらに続けてp n b fとタイプすればカーソルが上下、左右に移動するだろう。, p, n, f, bは1つのrepeat可能なキーシーケンスグループを形成するキーシーケンスなのだ。したがってこれら4つのキーシーケンスのいずれかをタイプした後の場合には、同一グループのキーシーケンスならキーシーケンスの最後の文字をタイプすれば何回でも繰り返すことができる。他のキーを何かタイプすれば繰り返しは終了して、タイプしたキーのデフォルトの動作を確認できるだろう。repeat可能なキーシーケンスグループは、C-h v devil-repeatable-keys RETとタイプすれば完全なリスト確認できる。

  5. repeat可能なキーシーケンスグループに属しているキーシーケンスが、1つのrepeat可能なキーシーケンスだけという場合もある。そのようなキーシーケンスの一例が, m ^だ。これはM-^に変換されて、カレント行と前の行を結合するコマンドを呼び出す。複数行のテキストバッファーで, m ^とタイプした場合でもカレント行と前の行が結合されるが、間を開けずに^をタイプすれば、行の結合を繰り返し行うことができる。他のキーをタイプすれば、繰り返しは終了する。

  6. , sとタイプするとDevilがそれをC-sに変換して、インクリメンタル検索が呼び出されるだろう。

  7. , m xとタイプするとDevilがそれをM-xに変換して、対応するコマンドが呼び出される。そうだ、, mM-に変換されているのだ。

  8. , m m sとタイプするとDevilがそれをC-M-sに変換して、正規表現ベースのインクリメンタル検索が呼び出される。, m mC-M-に変換されるキーシーケンスだからだ。

  9. , u , fとタイプするとDevilがそれをC-u C-fに変換して、カーソルは4文字前方に移動する。

  10. , u u , fとタイプすると16文字前方にカーソルが移動する筈だ。Devil自身の変換ルールと追加のキーマップにより、この入力キーシーケンスはC-u C-u C-f(カーソルを16文字前方に移動)のように振る舞う。

  11. カンマの後にスペースをタイプしたい場合には, SPCとタイプする。これはテキスト内で簡単にカンマをタイプできるようにするための、スペシャルキーシーケンスだ。このキーシーケンスは(マークをセットする場合に役に立ったかもしれない)C-SPCという意味での, SPCの使用を犠牲にしていることに注意。この犠牲が望ましくない場合については、セクション", SPC"をマークのセットにするにはを参照のこと。

  12. , z SPCとタイプするとDevilがそれをC-SPCに変換して、マークをセットするだろう。そうだ、, zC-に変換されるのだ。

  13. 同様に, RETとタイプすれば、カンマの後にenterキーをタイプできる。これもスペシャルキー。

  14. 単一のカンマは, ,でタイプできる。このスペシャルキーは、リテラルのカンマ1文字のタイプが必要なときに役に立つだろう。

  15. , h , kとタイプするとdevil-describe-keyが呼び出される。これは素のEmacsに含まれているdescribe-keyのDevil版を呼び出すスペシャルキーだ。キーの入力プロンプトが表示された後にDevilのキーシーケンス, x , fをタイプすると、Devilはそのキーシーケンスで呼び出される関数のドキュメントをを表示する。キーシーケンス, h kだとC-h kに変換されて、素のdescribe-keyが呼び出されることに注意。Devilキーシーケンスの, h , kが呼び出すのがdevil-describe-keyということだ。

5. カンマのタイプ

Devilをアクティブにするキーとして、Devilはカンマの採用という怪しげな道を選んだ。前セクションで述べたように、, x , fをタイプするとC-x C-fをタイプしたときと同じ効果が得られるのだ。ではリテラルのカンマはどうやって入力するか不思議に思うことは自然なことだろう

テキスト編集においてカンマを単独で入力することは、実際のところほとんどないだろう。カンマの後に間を開けずに1つのスペースか改行を入力することはよくある。これは通常のテキスト編集時ならよく当て嵌まる見立てだ。しかし既存の行の終端に1つのカンマ追加が必要なコードで作業する場合のように、同じ仮定が当てはまらない状況もあり得るだろう。

上記の仮定が当てはまるようなシナリオでは, SPCをタイプしてカンマとスペース、, RETをタイプしてカンマと改行を挿入する。

1つのカンマが必要なシナリオであれば、かわりに, ,タイプすればよい。

1つのカンマは, q ,とタイプして挿入することもできることに注意。このキーはC-q ,に変換されて、リテラルのカンマが挿入されるのだ。EmacsでC-qというキーシーケンスは、次に入力された文字を挿入するコマンドquoted-insertを呼び出す。しかしこれよりスペシャルキーシーケンス, ,の方が多分簡単だろう。

カンマキーを弄るのがどうにも腑に落ちないのであれば、Devilキーをよりしっくりする何らかのキーにいつでもカスタマイズできるという点についても、記しておく価値があるだろう。セクションインストールで触れたように、C-,でDevilモードを一時的に無効にしたり有効にすることもできる。

6. Devilのリーダー

以下にDevilがDevilキーシーケンスの読み込み、Emacsキーシーケンスへの変換、変換したキーシーケンスにバインドされたコマンドの実行をどのように行うかについて要点をまとめた。

  • Devilキー(デフォルトでは,)をタイプすることでDevilが起動、Devilキーシーケンスの読み取りを開始する。C-h v devil-key RETとタイプすればカレントのDevilキーを確認できる。
  • キーストロークをすべて読み取った後に、蓄積されたキーシーケンスがスペシャルキーかどうかをDevilがチェック、もしそうならスペシャルキーにバインドされているスペシャルコマンドを即座に実行する。このステップは入力キーシーケンスに変換ルールが適用される前に処理されることに注意。この方法によってDevilキーシーケンス, SPCはカンマとスペースを挿入している。C-h v devil-special-keys RETタイプすればスペシャルキー、キーにバインドされているコマンドのリストを確認できる。
  • ここまで蓄積されたキーシーケンスがスペシャルキーでなければ、DevilはDevilキーシーケンスを通常のEmacsキーシーケンスに変換する。通常のEmacsキーシーケンスがコンプリートキーシーケンスであり、それにバインドされているコマンドがあれば、そのコマンドを即座に実行する。これがDevilキーシーケンス, x , fC-x C-fに変換されて、それにバインドされたコマンドが実行される仕組み。変換されたキーシーケンスがコンプリートキーシーケンスであるもののコマンドがバインドされていなければ、Devilはそのキーシーケンスが未定義である旨を示すメッセージを表示する。C-h v devil-translations RETとタイプすれば変換ルールのリストを確認できる。
  • DevilキーシーケンスからEmacsキーシーケンスへの変換、バインドされているコマンドの実行に成功したら、キーシーケンスがrepeat可能なキーシーケンスかどうかをDevilがチェック、repeat可能なキーシーケンスにバインドされていれば、Devilキーシーケンスとしてタイプされたキーと同じグループに属すrepeat可能キーを、入力キーシーケンスの最後の文字だけで実行できるように、Devilが一時的なマップをセットする。, p p p f fで3行上、2文字前方に移動できるのは、この仕組みによる。C-h v devil-repeatable-keys RETとタイプすればrepeat可能なDevilキーシーケンスを確認できる。

変数devil-special-keysdevil-translationsdevil-repeatable-keysにはキーまたは値として文字列%kが含まれているかもしれない。これはdevil-keyにたいするプレースホルダ。スペシャルキー、変換ルール、repeatルールの適用中は、ルールを適用する前に%kはそれぞれ実際のdevil-keyの値に置き換えられる。

7. 変換メカニズム

以下はユーザーが入力したDevilキーシーケンスをEmacsキーシーケンスに変換するためにDevilが使用している変換メカニズムに関する要点の説明。

  • ユーザーからのキーベクター入力は、(describe-keykey-descriptionが生成する文字列のような)キー表記(key description: キー記述)へと変換される。たとえばユーザーがタイプした,x,f, x , fに変換される。
  • これで得られたキー表記は、シンプルな文字列置換によって変換される。この文字列の任意の部分がdevil-translationsのいずれかのキーとマッチしたら、そのキーに応じた数値へと置き換えられる。たとえば, x , fC- x C- f.に変換される。修飾キーの後の半端なスペースは、結果がC-x C-fになるようにDevilによって正規化される。
  • 上記で議論した文字列ベースのシンプルな置換によって得られるのが不正なEmacsキーシーケンスであれば、不正な結果とならないようにその置換はスキップされる。たとえば, m m ,にたいしてこのシンプルな文字列置換で処理するとC-M-C-が得られる(デフォルトの編ルールにより先頭の, m mC-M-、末尾の,C-に変換されるからである。しかしC-M-C-は無効なキーシーケンスなので、末尾の,からC-への変換はスキップされる。したがって入力, m m ,C-M-,へと変換されるのである。
  • 最後にDevilはキーシーケンスから、修飾キーC-と大文字アルファベットの両方を含むようなキーコードを探す。そのようなキーコードがあれば大文字は小文字をシフト修飾した文字なので、たとえば, m m Vにたいして上記の変換を行うとC-M-Vが得られるが、ここで説明した変換によりC-M-S-vが得られることになる。

8. デフォルトの変換ルール

デフォルトでDevilがサポートしているのはさまざまなタイプのキーシーケンスの入力から修飾キーを取り除くための、小さくて一風変わった一連の変換ルールである。変換ルールについてはC-h v devil-translations RETで確認できる。以下にデフォルトの変換ルールのデモンストレーションをいくつか示そう。明快な例から始めて、最後に向かうにつれて複雑な変換をお見せしていく。このサブセクション最後のパラグラフでは、いかにしてこれらのキーシーケンスを徐々に無理なく、日常の編集活動に取り入れていくかについてのガイドラインも示しておいた。

入力 置換後 備考
, s C-s Rule 1: ,C-に置き換え
, m x M-x Rule 2: , mM-に置き換え
, [ x C-[ x M-xと同じ
, m m s C-M-s Rule 3: , m mC-M-に置き換え
, m , M-, Rule 4: , m ,M-,に置き換え
, m z m M-m Rule 5: , m zM-に置き換え
, c , , C-c , Rule 6: , ,,に置き換え
, z SPC C-SPC Rule 7: , zC-に置き換え
, z z C-z 同上
, z , C-, 同上

, SPCをマークのセットに使用できないは、そのキーシーケンスが既にdevil-special-keysでスペシャルキーシーケンスとして予約済みであるという点に注意。利便性のためにDevilは, zC-に変換する。そうすれば, z SPCとタイプすればC-SPCに変換されるので、簡単にマークをセットできるだろう。

M-のタイプには, mを使うことができるが、M-を含んだキーシーケンスをタイプする別の方法に, [がある。これはC-[C-[ <key>に変換されるが、ESC <key>M-<key>と等価なのだ。

上の表で示したデフォルトの変換例は下にいくほど奇妙に見えるかもしれない。しかし初見で感じるほど独断的に決められた訳でもないのだ。この表は以下のような効果を期待して編成した:

  • Devilは,C-, mM-, m mC-M-に変換する

  • Devilキーで本当の,をタイプしたければ、このキーシーケンスを2回連続でタイプする必要がある。スペシャル文字を2回連続でタイプするということには、Devilキーの特別な意味を打ち消してリテラル形式が得られるようにするエスケープメカニズムとしての役目がある

  • , ,,に変換されるのでC-,をタイプするための、もう1つ別のエスケープメカニズムが必要になった。それが間にzをタイプするというエスケープメカニズムだ。つまりDevilキーシーケンスの, z ,C-,に変換される。

  • , m mC-M-に変換されるのでM-mをタイプするためのエスケープメカニズムも必要になる。そこで同じように間にzをタイプする、すなわち, m z mM-mに変換するというのが、このエスケープメカニズムなのだ。

以上がこのキーシーケンスを採用するに至った経緯の簡単なガイドラインになる。Devil初心者がこれらのすべてを覚えておく必要はない。とりあえず始めるにあたっては,C-, mM-に変換されることだけ理解していれば十分だ。使い始めてから, m mC-M-に変換されると学習すれば, m m s (C-M-s)や, m m f (C-M-f)といったより多くのキーシーケンスへの扉が開くだろう。 Devilを日常的に使用していく過程でこれらの初歩的ルールが網羅しないキーシーケンスに出会ったら、そのときは上記の表に立ち戻って新たな変換ルールを採用して欲しい。

9. DevilキーのDescribe

DevilはDevilキーシーケンスの記述に使用できるコマンドとしてdevil-describe-keyを提供する。素のEmacsにおいてC-h kで呼び出せるdescribe-keyコマンドと同じように機能する。Devil版のdevil-describe-keyはスペシャルキーシーケンス, h , kで呼び出すことができる。, h , kとタイプすると、キーシーケンスを読み込むためのプロンプトが表示されるだろう。何かDevilキーシーケンス、たとえば, x , fをタイプすれば、このキーシーケンスによって呼び出される関数のドキュメントをDevilが即座に表示するのだ。

C-x C-fのような素のEmacsキーシーケンスのドキュメント参照にも, x , f (devil-describe-key)を使用できることに注意。

Devilキーシーケンスである, h kの方は依然としてC-h k (素のEmacsのdevil-describe-key)を自由に呼び出せることにも注意して欲しい。

10. ボーナスのキーバインディング

Devilはglobal-devil-modeによってグローバルにDevilが有効化されている場合に限り、以下のキーバインディングを追加提供する:

  • isearch-mode-mapへのDevilキーの追加(インクリメンタル検索でもDevilキーシーケンスが機能する)

  • universal-argument-moreへのuの追加(uだけでユニバーサル引数コマンドC-uを繰り返すことができる)

繰り返しになるが、これらの機能はglobal-devil-modeでグローバルにDevilを有効化した場合のみ利用できる。

devil-modeでローカルにDevilを有効にした場合には、これらの機能は利用できない。

11. カスタム設定の例

以下の例において(require 'devil)呼び出しについては、ELPAやMELPAのようなパッケージアーカイブ経由でDevilをインストールした場合には不要かもしれない。Devilパッケージには適切なautoloadがあるので、Devilモードを有効化することで自動的にロードされる筈だ。しかしここでは万全を期すために、require呼び出しも含めてある。

11.1 ローカルモード

セクション,インストールではDevilモードをグローバルに有効にする方法を紹介したが、ここではローカルに有効にする方法について説明しよう。以下はテキストバッファーでのみローカルにDevilを有効化する初期化コードの例だ。

 (require 'devil)
 (add-hook 'text-mode-hook 'devil-mode)
 (global-set-key (kbd "C-,") 'devil-mode)

ただしシームレスなDevil経験は得られないのでお勧めはしない。たとえばこの例のようにテキストバッファーでローカルにDevilを有効にすると、ミニバッファーで, x , fをタイプしてfind-fileを起動できなくなる。ミニバッファーでDevilキーシーケンスが使えなくなるのだ。更に前のセクションで述べた機能は、Devilモードがグローバルに有効なときしか使用できない。

11.2 バッファーのデフォルトモード

global-devil-modeで有効にするだけで、通常ユーザーが目にするであろうすべてのバッファーでDevilを使うには十分かもしれないが、従来のセットアップフックをバイパスして作成されたバッファーについては、デフォルトではモードは有効にならない。この問題にもっとも遭遇しやすい場所が、Emacsのスタートアップスクリーンだ。このバッファーはユーザーのinitファイルの処理後に作成されるバッファーだが、Devilを有効化する如何なるフックも実行しないのだ。

この特定的な問題を解決するには必要なフックを実行するようにスタートアップスクリーンをカスタマイズするか、あるいはバッファーの作成後にDevilを有効化するようにdisplay-startup-screenにadviseするかのいずれかをお勧めする。以下はglobal-devil-modeを有効にしてデフォルトのEmacsスタートアップスクリーンでアクティブにする例だ。

 (require 'devil)
 (global-devil-mode)
 (advice-add 'display-startup-screen
             :after (lambda (&optional _) (devil-mode 1)))

これで出会う可能性がある問題は解決する筈だ。しかしあなたが正真正銘すべてのバッファーでDevilをアクティブにする必要があるときはどうするか? devil-global-sets-buffer-defaulttのときにglobal-minor-modeを呼び出すと、新たに作成されるすべてのバッファーではデフォルトでDevilが有効になる。この機能を有効にするのは魅力的に思えるかもしれないが、すべてのバッファーが対話的であることを意図している訳ではないこと、適用されるかもしれないフックをマイナーモードが外すのはおそらく意図があってのことである点を考慮すること。推奨はしないが、以下はすべてのバッファーでデフォルトでDevilをアクティブにする例を示す。

  (require 'devil)
  (setq devil-global-sets-buffer-default t)
  (global-devil-mode)

11.3 外観のカスタマイズ

以下の初期化コードはモードラインとDevilプロンプトにDevilスマイル(😈)を表示させるカスタマイズ例だ。

  (require 'devil)
  (setq devil-lighter " \U0001F608")
  (setq devil-prompt "\U0001F608 %t")
  (global-devil-mode)
  (global-set-key (kbd "C-,") 'global-devil-mode)

11.4 ", SPC"をマークのセットにするには

デフォルトの設定では, SPCはカンマとスペースを挿入するスペシャルキーとして予約済みだ。このデフォルトはさまざまなコンテキストにおいてカンマのタイプを簡単にするためだ。しかしこれは, SPCC-SPCに変換されないという意味でもある。つまり, SPCではマークをセットできない。その代替えとしてデフォルトの変換ルールでは、マークをセットする方法として, z SPCを提供している。

リテラルのカンマを挿入するためにスペシャルキー, ,をタイプすることが面倒でなければ、以下の設定を使えば, SPCでマークがセットできる:

  (require 'devil)
  (global-devil-mode)
  (global-set-key (kbd "C-,") 'global-devil-mode)
  (assoc-delete-all "%k SPC" devil-special-keys)

これによりdevil-special-keysからスペシャルキー, SPCが削除されるのでC-SPCに変換されてset-mark-commandを呼び出すようになる筈だ。

11.5 カスタムDevilキー

以下は違うDevilキーを使うようにDevilをカスタマイズする初期化コードの例を示す。

  (require 'devil)
  (global-devil-mode)
  (global-set-key (kbd "C-;") 'global-devil-mode)
  (devil-set-key (kbd ";"))

この例ではDevilキーとして(もしかしたらDevilキーに相応しいより邪悪な別解かもしれない)セミコロンをセットしている。この設定の場合には; x ; fとタイプするとDevilがそれをC-x C-fに変換するだろう。

11.6 Devilキーを別のキーにカスタマイズする

以下の例では他のDevilを使用するようにカスタマイズする別の初期化コードをお見せしよう。

  (require 'devil)
  (global-devil-mode)
  (global-set-key (kbd "C-<left>") 'global-devil-mode)
  (devil-set-key (kbd "<left>"))
  (dolist (key '("%k SPC" "%k RET" "%k <return>"))
    (assoc-delete-all key devil-special-keys))

上記の例では左矢印キーをDevilキーにセットしている。この設定の場合だと<left> x <left> fとタイプすればDevilがC-x C-fに変換する。スペシャルキー<left> <left>が元の<left>を生成する効果は同じだ。

上記の例では最早有用とはいえなくなったスペシャルキーもいくつか削除している。特に<left> SPCがスペシャルキーとして予約されなくなったので、これをマークのセットに使用することができる。

11.7 複数のDevilキー

このパッケージはデフォルトとしてカンマ(,)を、それも唯一のDevilキーとして提供しているが、複数のDevilキーをサポートするためのモードの拡張を妨げるものはない。たとえばDevilをアクティブにすることで,C-の役目をもたせるだけではなく、Devilをアクティブにすれば.M-の役目をもつようにしたいと考えたとしよう。この目標を達成するための開始点として以下の初期化コードを使用して、その後は要件に基づいて更にカスタマイズするということもできるだろう:

  (require 'devil)
  (global-devil-mode)
  (define-key devil-mode-map (kbd ".") #'devil)
  (add-to-list 'devil-special-keys `(". ." . ,(devil-key-executor ".")))
  (setq devil-translations '((", z" . "C-")
                             (". z" . "M-")
                             (", ," . ",")
                             (". ." . ".")
                             ("," . "C-")
                             ("." . "M-")))

この設定なら前と同じように, x , fC-x C-fをタイプできる。しかし今度は. xM-xをタイプできるのだ。そして, . sC-M-sとか、他にも組み合わせはあるだろう。さらに, ,はリテラルのカンマ、. .はリテラルのドットを挿入する。更に, z ,C-,. z .M-.もタイプできるようになった。

デフォルトのDevilの設定ではDevilアクティブ化のキーは1つだけ(,)であること、そしてアクティブにするキーをより多く追加することによって、通常の編集タスク中にDevilがでしゃばる機会がより多くなることに注意。Devilのアクティブ化のために予約済みのキーはすべてデフォルトの機能性を失い、そのキーに割り当てられているデフォルトの機能を呼び出すためには、何らかの余計な手間が必要になる(たとえば上述のように1つの.の挿入に.を2回繰り返す等)。したがって、Devilキーはできるだけ少ない個数に保っておいたほうがよいだろう。

11.8 すべてのキーをrepeat可能に

Devilにはデフォルトでrepeat可能とみなすキーシーケンスの小さなリストが用意されている。これは変数devil-repeatable-keysで定義されているリストだ。このリストの内容はC-h v devil-repeatable-keys RETで確認できる。たとえばこのリストにrepeat可能なキーシーケンスのグループ("%k p" "%k n" "%k f" "%k b")が定義されているとしよう。DevilとEmacsのデフォルトのキーバインディングが変更されていなければ、この定義は, pをタイプした後にカーソルを前の行に移動すること、pをタイプする度に何度もこれを繰り返せることを意味している。間を開けずにfをタイプすれば1文字分カーソルが右に移動する。この繰り返しはグループ内のキーシーケンスであればどれでも、最後の文字をタイプするだけで繰り返すことができる。何か別のキーをタイプすれば繰り返しが終わって、タイプしたキーのデフォルトの動作に戻るだろう。

変数devil-all-keys-repeatabletにセットすれば、すべてのキーシーケンスを繰り返しできるようにすることも可能だ。以下はその設定例:

  (require 'devil)
  (setq devil-all-keys-repeatable t)
  (global-devil-mode)

この設定でも上述のrepeat可能なキーシーケンスグループの機能は失われない。しかしこれらのキーシーケンスに加えて、最終的にはEmacsコマンドを実行するようなDevilキーシーケンスすべてが繰り返し可能になったのだ。つまりdevil-all-keys-repeatableには属していないがEmacsコマンドを呼び出すDevilキーシーケンスはrepeat可能、すなわち間を開けずにそのキーシーケンスの最後の文字で繰り返すことができる。

正規のEmacsキーシーケンスに変換されること、変換した結果としてEmacsコマンドが実行されるようなDevilキーシーケンスだけが繰り返し可能であることに注意。devil-special-keysで定義されているキーシーケンスが繰り返し可能になることはないのだ。

11.9 Repeatモードとの相互作用

Devilのrepeat可能なキーの機能は、Emacs 28.1で導入されたrepeat-modeと類似する機能だ。以下はDevilのrepeat可能キーを無効にして、repeat可能なコマンドの定義にrepeat-modeを用いる設定の例を示す。

  (require 'devil)
  (global-devil-mode)
  (setq devil-repeatable-keys nil)

  (defvar movement-repeat-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "p") #'previous-line)
      (define-key map (kbd "n") #'next-line)
      (define-key map (kbd "b") #'backward-char)
      (define-key map (kbd "f") #'forward-char)
      map))

  (dolist (cmd '(previous-line next-line backward-char forward-char))
    (put cmd 'repeat-map 'movement-repeat-map))

  (repeat-mode)

ここで1行上にカーソルを移動するためにC-pをタイプしたとすると間を開けずにpをタイプすれば繰り返し移動できるしnbfでもカーソルを下、左、あるいは右に移動できる。

repeatモードはDevilと良好に機能するので、上の設定であれば前の行へカーソルを移動するために, pをタイプしたらpnbfで上下左右にカーソルを移動できる。

実際のところrepeatモードを使用中にDevilのrepeatキーを不要にする必要はない。両者は共存できるのだ。しかし両者の間に存在する特定の差異によっては、驚くべき結果となることもある。たとえば以下のような設定だと:

  (require 'devil)
  (global-devil-mode)

  (defvar movement-repeat-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "p") #'previous-line)
      (define-key map (kbd "n") #'next-line)
       map))

  (dolist (cmd '(previous-line next-line))
    (put cmd 'repeat-map 'movement-repeat-map))

   (repeat-mode)

これでDevilのrepeat可能キーとrepeatモードの両方がアクティブになった。ここで, pをタイプすると、pnでカーソルを繰り返し上下できる。今度は繰り返しを実現しているのはrepeatモードだ。更に, pをタイプした後であれば、bfでカーソルを繰り返し左右に移動できる。こちらを実現しているのはDevilモードだ。repeatモードが処理するrepeat可能コマンドと、Devilモードが処理するrepeat可能キーとの違いは、エコーエリアを注視すれば判るだろう。pを繰り返しはrepeatモードによって処理されて、エコーエリアには"Repeat with p, n"のメッセージが確認できる筈だ。しかしDevilが処理するbを繰り返した場合には、Devilがrepeat可能を暗黙にセットアップするのでこのようなメッセージを目にすることはないだろう。

11.10 Repeatモードとの比較

前のセクションではrepeat可能なキーシーケンスをDevil、およびEmacsのバージョン28.1以降で同梱されるようになったrepeatモードそれぞれが実現する度合いをデモンストレーションした。

とはいえDevilのrepeat可能キーとrepeat-modeの間には厳然たる違いが存在する。repeatモードが提供するのはrepeat可能なコマンドだが、Devilがサポートしているのはrepeat可能なキーであるという点だ。この重要な違いにより、Devilでrepeat可能キーを構成するほうがほぼ間違いなく簡単なのだ。この違いをキーシーケンスM-eで実証してみよう。このキーシーケンスにはデフォルトでは、グローバルマップでforward-sentenceコマンドにバインドされている。しかしOrgモードではorg-forward-sentenceにバインドされているキーシーケンスだ。これに相当するDevilキーシーケンスは, m eであり、これはDevilではrepeat可能なキーシーケンスである。したがって, m eをタイプした後ならtextモードと同じくOrgでも、e e eと間を開けずにタイプすることによって、カーソルを前方に連続して移動させることができるのだ。repeat モードで同じ挙動を実現するためには、以下のような設定が必要になるだろう:

  (require 'devil)
  (global-devil-mode)
  (setq devil-repeatable-keys nil)
  
  (defvar forward-sentence-repeat-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "e") #'forward-sentence)
      map))
  
  (defvar org-forward-sentence-repeat-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "e") #'org-forward-sentence)
      map))
  
  (put #'forward-sentence 'repeat-map 'forward-sentence-repeat-map)
  (put #'org-forward-sentence 'repeat-map 'org-forward-sentence-repeat-map)
  
  (repeat-mode)

どちらもM-eにバインドされている2つのコマンド用に、repeatモードをどのように設定する必要があるかに注目して欲しい。この設定であればtextモードとOrgモードの両方で、, m eをタイプした後にe e eと続けて前方に複数センテンス移動できるだろう。ただしM-eに何らかののコマンドをバインドしている、他のモードにたいするrepeatモードの設定を忘れていなければよいが。たとえばそう、M-eにコマンドc-end-of-statementをバインドしているCモードのようなモードのことだ。上記の設定ではこのコマンドをe e eで繰り返すには足りていないらしい。

しかしDevilであればすべてのモードにおいてM-eにバインドされているコマンドを繰り返すことができるのだ。Devilは単に特定のモードでバインドされたコマンドをrepeat可能にするのではなく、, m eというキーシーケンス自体をrepeat可能にする。したがってDevil独自のrepeat可能なキーシーケンスのサポートを使用すればアクティブなモード、あるいはキーシーケンス, m eにバインドされているコマンドが何であれe e eでそのコマンドを繰り返すことができる。

12. どうしてこうなった?

わざわざこのようなモードを作成、使用するのはなぜだろう? 他のまともな人のようにcaps lockctrlにリマップするだけでよいのでは? そんなに修飾キーを避けることが重要なのであれば、GodモードやEvilモードのようなモードを使えばよいではないか?

まずGodモードとEvilモードはいずれもモーダル編集モードであることが理由の1つだ。一方DevilではEmacsにおける非モーダル編集のエクスペリエンスは保たれる。

当初Devilは小さな実験として始まった。カンマのような重要なキーを使って修飾キーを指定することが災いの元であることは、初めから明らかだった。しかしどこまでそれが可能なのかを試してみたかったのだ。そして数日使用してみて、Emacsで行うすべてのことに常用できることが判った。

この実験は右側にctrlキーがないMacbookのキーボードによって動機づけられた部分もある。タッチタイプを行う者としてはC-xC-sC-rC-dC-fC-wC-aC-eのような修飾キーと被修飾キーの両方を左手で押下する必要があるキー組み合わせをタイプするのに不便だと感じていたのだ。caps lockctrlのように振る舞うようにリマップするという方法にたいしても、特に食指を動かされることはなかった。依然としてC-xC-aのようなキー組み合わせは、修飾キーと被修飾キーの両方を左手で押下する必要があるからだ。ctrlとして振る舞うように、caps lockenterの両方をリマップする人が沢山いるのは知っていた。素晴らしい解決策だとは思ったが、わたしが作業を行っているさまざまなオペレーティングシステムすべてにおいて、これをシームレスに動作させるために要される作業を行う気にはなれなかったのだ。

気まぐれで始まった小さな実験ではあるが、数年を経て少なくともわたしにとっては非常に効果的だということが判った。純粋にElispだけで実装したので、外部への依存性がない解決策という点でも気に入っている。これが役に立つと思うかもしれない他の人たちがいるかもしれないという場合に備えて、この解決策をマイナーモードという形式で共有する。

13. Godモードとの比較

Godモードはモーダル編集エクスペリエンスを提供するがDevilは違う。Devilはユーザーが新たにキーバインディングを学習する必要がないという、Godモードと同じ基本哲学をもっている。しかしGodモードのように挿入モードとコマンドモードの間を厳格に区別しないのがDevilだ。そのかわりにDevilはアクティベーションキー(デフォルトでは,を待機して、そのキーがタイプされたらアクティブになりキーの横取りと変換を行い対応するコマンドを実行して引っ込むのだ。そうすることにより、Devilは素のEmacsにおける非モーダル編集エクスペリエンスを保とうと試みている。

god-modeのgod-execute-with-current-bindings関数を使えば、非モーダルな編集エクスペリエンスを部分的に再現可能であることにも触れておいたほうがよいだろう:

 (global-set-key (kbd ",") #'god-execute-with-current-bindings)

この設定であれば, x fをGodモードがC-x C-fに変換する。同じく, g xM-x, G sC-M-xを呼び出すだろう。これならGodモードでも非モーダル編集エクスペリエンスを手に入れることができる。ただしこのエクスペリエンスはミニバッファーにたいしてはシームレスに拡張されない。Devilの方はDevilキー変換をミニバッファーにも拡張する。

さらにGodモードではctrl修飾キーの振る舞いが粘着性(sticky)をもつことにも注意を要する。つまりキーシーケンス全体が完了するまでは、修飾キーが自動的にアクティブなままになるのだ。したがって上記の例では, x fのように一度だけ,をタイプしてC-x C-fを呼び出している。しかしこの粘着性の振る舞いは、C-x C-f (find-file)とC-x f (set-fill-column)のようなキーシーケンスを区別する何らかの手段が必要であることを暗に含んでいる。GodモードはSPCを導入するという解決策によって、修飾キーの非アクティブ化を行っている。しかしDevilの方は少しキーをタイプするコストより、シンプルなキーシーケンスのメリットの方が勝るという考えから、修飾キーに粘着性をもたせていない。つまり, x , fC-x C-f, x fC-x fに変換される。

まとめよう。Devilと比較すると主に4つの違いがある:

  • 最初から非モーダル編集エクスペリエンスを提供するか

  • 同じ編集エクスペリエンスをミニバッファー、インクリメンタル検索等にシームレスに拡張するか

  • 洗練された任意のキー変換を可能にする、文字列置換を用いたキーシーケンス変換(チャレンジャー向け)

  • 修飾キーにたいする粘着的振る舞いの選択

これらの違いにより、Devilを使う方がGodモードより簡単な人もいれば、扱いにくくなる人もいるだろうし、これは好みの問題だと思う。

14. FAQ

  • Devilキーのデフォルトにカンマ(,)を選んだのはなぜ? セミコロン(;)の方がホームポジションにあるので良くない?

    意見はさまざまだろう。わたしがこのマイナーモードの作者兼メンテナーとして選んだデフォルトのDevilキーがカンマだったというだけだ。ほとんどのキーボードではセミコロンはホームポジションにありカンマキーは違うのだが、わたしにはセミコロンに小指を届かせるために要する水平移動よりも、カンマキーに中指を届かせる垂直移動のほうが楽だったからだ。
    タッチタイプを行う際に、わたしにはアイドル時にホームポジションの8つのキーに指をおいている。セミコロンのタイプに必要な水平移動は、手首の有意な角度移動をともなうのだ。中指を曲げてカンマキーをタイプすれば、手首の負担を回避できる。このデフォルトが気に入らなければ、セミコロンや何であれあなたが選択する任意のキーをDevilキーにカスタマイズするのはとても簡単だ。方法についてはセクションカスタムDevilキー、その後の数セクションを参照して欲しい。

  • カンマをタイプする必要があるとき毎回, ,をタイプするのが全然苦ではない。, SPCを解放してset-mark-commandを呼び出すようにしてもいい?

    問題ない。devil-special-keysからスペシャルキー, SPCを削除するだけだ。これを行う方法についてはセクション", SPC"をマークのセットにするにはを参照のこと。

  • Devilキーをstickyなキー、つまり粘着的にできない? , x , fではなく, x fC-x C-fを呼び出せるようにしたいんだ。

    Devilはstickyキーをサポートしない。たとえばもしDevilが, x fC-x C-fに変換するとして、C-x fはどうやって呼び出すのか? C-x C-fC-x fの間にある曖昧さを解決する何らか手段が必要だ。異なるツールがこの2つのキーシーケンスの曖昧さにたいして、異なるアプローチを採択したのだ。Godモードはx fC-x C-fx SPC fC-x fに変換する。すなわちC-修飾はデフォルトでstickyであり、その粘着性を解消するためにgod-modeではSPCをタイプする必要がある。これによりC-x C-fのような一部のキーシーケンスは短くなったが、C-x fのようなキーシーケンスは長くなった。

    DevilはDevilキーを非stickyとして扱うので、stickyあるいは非stickyな挙動を切り替える特異なルールを追加で定める必要がないのだ。したがってC-x C-fC-x fのようなキーシーケンスの曖昧さはDevilであれば, x , fC-x C-f, x fC-x fに変換することで解決されている。追加で少し多くタイプするというコストを要する場合もあるものの、変換ルールはより単純になる。Devil故に追加でカンマのタイプが必要な場合のほとんどは、カンマがstickyであれば回避できたであろう。しかしそれ以外の場合ではDevilが修飾キーを非stickyにしている故に、余計なキーのタイプを不要にしているのだ。

  • Devilの方がgod-modeより簡単にできることって何がある?

    Devilの方がgod-modeより簡単である必要はない。両者は違うものだ。好みはさまざまなのでDevilが簡単だと思う人もいれば、god-modeの方が使いやすいと思う人もいるだろう。2つのモードの違いについて知りたければ、セクションGodモードとの比較を参照して欲しい。

  • 設定ファイルで関数global-devil-modeを呼び出したけど、EmacsがDevilをオープンしてもアクティブにならないみたい、何が悪いんだろう?

    一番有り得そうなのは最初バッファーがに表示されたのがEmacsのデフォルトのスタートアップスクリーンの場合だろう。このスクリーンはDevilを適用する機会となるフックを何も実行せずに作成されるのだ。詳細と解決策についてはセクションバッファーのデフォルトモードを参照して欲しい。

15. 結び

Devilとはキーシーケンスを変換するマイナーモードだ。Devilはモーダル編集に頼ることなく、修飾キーいらずの編集エクスペリエンスを提供するためにこの変換能力を活用している。Devilが拘っているのは、素のEmacsにおける非モーダルの編集体験だ。このモードは修飾キーなしでEmacsを簡単に使えるようにするという風変わりな実験として記述された。しかし結果としては客観的に見て非常に使いやすく役に立つモードが得られたと思う。Devilを使って快適に感じるかもしれないし、あるいは酷いアイデアと思うかも、もしかしたら役には立つがでしゃばりなモードだとも思うかもしれない。そのような場合には好みに応じてDevilの構成を変更するための、大量のカスタマイズ可能なオプションが存在する。何かサポートが必要だったり問題を見つけたら、issueを作成して教えて欲しい。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?