週休3日サラリーマンのブログ

空気を読まないサラリーマンをやってます。1980生まれ男です。既婚。2011年生まれ息子、2013年生まれ娘あり。

MacでEmacsキーバインドを使うには、自分専用の設定ファイル作成が必須だ(Karabiner-Elements)

controlキーとアルファベットキーの組み合わせでカーソルを上下左右・文頭・文末に移動したり、PageDown、Delete、Backspace、等々…かなり多様な操作を可能にする、いわゆる「Emacsキーバインド」。これを使いこなすとホームポジションから手を離さずにできる操作がかなり増えるため、非常に便利だ。

おそらくMacユーザーの10人に1人も使っていないと思われるこのマニアックなEmacsキーバインドを、Macはかたくなに標準サポートし続けている。
Emacsキーバインド愛好者にとってはありがたいことと思いきや、しかし、これが実はあまり使い物にならない。なぜかというと、アプリによってサポートしていなかったり、またはサポートしていてもアプリによって挙動が微妙に異なるからだ。
Emacsキーバインドを活用して、標準アプリのメモ帳で上下左右のカーソル移動、文頭・文末移動、Delete、Backspaceを存分に使ってテキスト編集をした直後、Googleスプレッドシードのアプリに切り替えたらこれらの操作の一部しか使えなかったりする。すると、便利さが半減どころか、十分の一くらいになってしまうのだ。

自分が利用するアプリ全てにおいて、完全に自分が所望する挙動をするように設定したい。
そのために「Karabiner-Elements」というキーバインド設定アプリを使う。世界中に多数の愛用者がいる有名アプリで、無料だ。

「Karabiner-Elements」の設定を行う。

まず、これは英語キーボードの場合のみに必要となる設定だが、"A"の左隣に位置するCapslockキーをcontrolキーとして扱う。日本語キーボードの場合は元々"A"の左隣にcontrolキーが位置しているので何もする必要はない。

・Karabiner-Elements > Simple modifications > (From key: caps_lock, To key: left_control)

次に、Karabiner-Elements公式サイトに公開されている、標準的なEmacsキーバインド設定をダウンロードして使用してみる。

(1) Karabiner-Elements > Complex modifications > [Add rule] ボタン押下
(2) [Import more rules from the Internet] ボタン押下
(3) Emulation Modes > Emacs key bindings (rev 12) を[Import]
(4) Karabiner-Elements > Complex modifications > Emacs key bindings (rev 12) > Emacs key bindings [control+keys] を[Enable]


しかし使ってみると分かるのだが、これが全然ダメなのだ。いや、この設定ファイルの作者にとっては自分の要望を完璧に満たすものなのだ、たぶん。しかし、私は彼が使わないアプリを使うこともあるし、【control + T】にタブ移動操作を割り当てたり【control + R】にPageUpを割り当てるのはEmacsの標準的なキーバインドではなくて、私の好む独自アレンジだから、そのような設定が彼の設定ファイルに含まれている事は望めない。

だから、MacEmacsキーバインドを使うには、自分専用の設定ファイル作成が必須なのだ。
完全に自分好みの設定だけをひとつひとつ加えていき、自分にとって完璧なKarabiner-Elementsの設定ファイルを作成する。

設定ファイルの書き方は、先ほどダウンロードした「Emacs key bindings (rev 12)」が参考になる。この設定ファイルはインターネットからインポートした際に「~/.config/karabiner/assets/complex_modifications/1619530943.json」に格納されている。自分の設定ファイルも同じパスにJSONファイルとして格納しておくとKarabiner-Elementsに認識され、設定から「Enable」することができる。
設定ファイルひとつの中に、いくつかの設定単位を設けてそれぞれにオン・オフすることができるが、私は設定ファイルひとつに対してひとつの設定単位しか設けず、さらにその設定単位にもあまり多くの設定を詰め込まないようにした。
理由は、JSONファイルを手動でガシガシ編集する際にカンマの有無などの構文エラーを埋め込みやすく、構文エラーがあるとKarabiner-Elementsは当該設定ファイルを丸ごと無視するからだ。設定ファイルひとつ当たりの文字数が少なければ構文エラー箇所を見つけやすいのでデバッグに向く。

とりあえず自分用Emacsキーバインド設定ファイルとして、以下の4ファイルを作成した。

(1) My_Emacs1.json (カーソル移動)
(2) My_Emacs2.json (PageUp・PageDown・文頭移動(Home)・文末移動(End))
(3) My_Emacs3.json (タブ移動・タブ閉じる)
(4) My_Emacs4.json (Delete・Backspace)

コンセプトと設定内容概略は次の通り。

(1) My_Emacs1.json (カーソル移動):

これはEmacsキーバインドの最も有名で重要な「カーソル移動」の設定。設定的には単純で、当該キーの組み合わせが押された場合、上下左右カーソルのキーコードを発生させる。
・【command + P】:[上カーソル]
・【command + N】:[下カーソル]
・【command + B】:[左カーソル]
・【command + F】:[右カーソル]

{
  "title": "My Emacs1",
  "maintainers": [
    "xxx"
  ],
  "rules": [
    {
      "description": "(My_Emacs1) カーソル移動",
      "manipulators": [
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Pで上カーソル",
          "type": "basic",
          "from": {
            "key_code": "p",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift", "option" ]
            }
          },
          "to": [
            { "key_code": "up_arrow" }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Nで下カーソル",
          "type": "basic",
          "from": {
            "key_code": "n",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift", "option" ]
            }
          },
          "to": [
            { "key_code": "down_arrow" }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Bで左カーソル",
          "type": "basic",
          "from": {
            "key_code": "b",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift", "option" ]
            }
          },
          "to": [
            { "key_code": "left_arrow" }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Fで右カーソル",
          "type": "basic",
          "from": {
            "key_code": "f",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift", "option" ]
            }
          },
          "to": [
            { "key_code": "right_arrow" }
          ]
        }
        
        
      ]
    }
  ]
}

(2) My_Emacs2.json (PageUp・PageDown・文頭移動(Home)・文末移動(End)):

「PageUp・PageDown・文頭移動(Home)・文末移動(End)」操作をこの設定ファイルで規定した。
キー割り当ては"A"に文頭移動(Home)、"E"に文末移動(End)、"V"に[PageDown]とするのはEmacsキーバインドで鉄則だが、PageUpにどのキーを割り当てるのかは私が知る限りではあまり統一されていない。キーボード上で"V"との位置関係と"Reverse"のイメージで"R"に[PageUp]を割り当てるのが私のやり方。
発生させるキーコードは少し工夫が必要。[PageUp]、[PageDown]はそのままでよいが、文頭移動、文末移動と[Home]、[End]キーコードは対応しない場合がある。例えば、私はEvernoteGoogle Chromeでアプリ化して使っているのだが、この編集画面で[Home]、[End]キーコードを送ると文書の先頭や末尾に移動してしまうため、このアプリに限っては文頭・文末移動を実現するには[command + 左カーソル]、[command + 右カーソル]キーコードを送る必要がある。その他のアプリもいちいちアプリ名指定で[Home]、[End]キーコードを送るようにする。

・【command + R】:[PageUp]
・【command + V】:[PageDown]
・【command + A】:文頭移動 ([Home]または[command + 左カーソル])
・【command + E】:文末移動 ([End]または[command + 右カーソル])

{
  "title": "My Emacs2",
  "maintainers": [
    "xxx"
  ],
  "rules": [
    {
      "description": "(My_Emacs2) PageUp・PageDown・文頭移動(Home)・文末移動(End)",
      "manipulators": [
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+VでPageDown",
          "type": "basic",
          "from": {
            "key_code": "v",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            { "key_code": "page_down" }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+RでPageUp",
          "type": "basic",
          "from": {
            "key_code": "r",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            { "key_code": "page_up" }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+AでHome",
          "type": "basic",
          "from": {
            "key_code": "a",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            { "key_code": "home" }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "file_paths": [
                "スプレッドシート\\.app"
              ]
            }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Aでcommand+←",
          "type": "basic",
          "from": {
            "key_code": "a",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            {
              "key_code": "left_arrow",
              "modifiers": [ "left_command" ]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "file_paths": [
                "Evernote\\.app"
              ]
            }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+EでEnd",
          "type": "basic",
          "from": {
            "key_code": "e",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            { "key_code": "end" }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "file_paths": [
                "スプレッドシート\\.app"
              ]
            }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Eでcommand+→",
          "type": "basic",
          "from": {
            "key_code": "e",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            {
              "key_code": "right_arrow",
              "modifiers": [ "left_command" ]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "file_paths": [
                "Evernote\\.app"
              ]
            }
          ]
        }
        
      ]
    }
  ]
}

(3) My_Emacs3.json (タブ移動・タブ閉じる):

「タブ移動・タブ閉じる」操作をこの設定ファイルで規定した。
キー割り当ては"T"にタブ移動、"W"にタブ閉じる、を割り当てた。
発生させるキーコードはアプリによって異なるので自分が使う全アプリについて調べて設定する。

・【command + T】タブ移動 # shift同時押しで逆方向移動
・【command + W】タブ閉じる

{
  "title": "My Emacs3",
  "maintainers": [
    "xxx"
  ],
  "rules": [
    {
      "description": "(My_Emacs3) タブ移動・タブ閉じる",

      "manipulators": [
 
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Tでタブ移動(control+Tab)",
          "type": "basic",
          "from": {
            "key_code": "t",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            {
              "key_code": "tab",
              "modifiers": [ "control" ]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "bundle_identifiers": [
                "^com\\.apple\\.Safari$",
                "^com\\.google\\.Chrome$",
                "^com\\.microsoft\\.VSCode$"
              ]
            }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Tでタブ順方向移動(option+下カーソル)",
          "type": "basic",
          "from": {
            "key_code": "t",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock" ]
            }
          },
          "to": [
            {
              "key_code": "down_arrow",
              "modifiers": [ "option" ]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "file_paths": [
                "スプレッドシート\\.app"
              ]
            }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+shift+Tでタブ逆方向移動(option+上カーソル)",
          "type": "basic",
          "from": {
            "key_code": "t",
            "modifiers": {
              "mandatory": [ "control", "shift" ],
              "optional": [ "caps_lock" ]
            }
          },
          "to": [
            {
              "key_code": "up_arrow",
              "modifiers": [ "option" ]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "file_paths": [
                "スプレッドシート\\.app"
              ]
            }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+Wでタブ閉じる(command+W)",
          "type": "basic",
          "from": {
            "key_code": "w",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "shift" ]
            }
          },
          "to": [
            {
              "key_code": "w",
              "modifiers": [ "command" ]
            }
          ],
          "conditions": [
            {
              "type": "frontmost_application_if",
              "bundle_identifiers": [
                "^com\\.apple\\.Safari$"
              ]
            }
          ]
        }
        
      ]
    }
  ]
}

(4) My_Emacs4.json (Delete・Backspace):

「Delete・Backspace」操作をこの設定ファイルで規定した。
キー割り当てはEmacsの定石通り、"D"に[Delete]、"H"に[Backspace]を設定した。
アプリによって発生させるキーコードを変える必要もない。

・【command + D】Delete
・【command + H】Backspace

{
  "title": "My Emacs4",
  "maintainers": [
    "xxx"
  ],
  "rules": [
    {
      "description": "(My_Emacs4) Delete・Backspace",
      "manipulators": [
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+DでDelete",
          "type": "basic",
          "from": {
            "key_code": "d",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "option" ]
            }
          },
          "to": [
            { "key_code": "delete_forward" }
          ]
        },
        {
          "description": "ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー",
          "description": "control+HでBackspace",
          "type": "basic",
          "from": {
            "key_code": "h",
            "modifiers": {
              "mandatory": [ "control" ],
              "optional": [ "caps_lock", "option" ]
            }
          },
          "to": [
            { "key_code": "delete_or_backspace" }
          ]
        }
        
      ]
    }
  ]
}

以上で設定ファイルの最初期バージョンが完成。
Karabiner-Elementsの設定から「Enable」し、とりあえず1週間くらい使用して想定通りに動作することが確認できた。

今回作成した設定ファイルは、まだ必要最低限の設定しか作り込んでいない状態だ。使用頻度の低いアプリについては動作確認していないし、「kill・yank」「Undo・Redo」などはまだ設定していない。
しかしこれから作り込んでいくベースとなる設定ファイルが出来たことがきわめて大きい。これからは、Macを使っている時に想定通りEmacsキーバインドが働かなかったり、「kill・yank」「Undo・Redo」を設定して使いたいな、と思い立ったときに3分くらいで設定ファイルを編集したり追記したりできる。
この「改善スパイラル」に入るともう勝ったも同然だ。たぶん半年後には、私は魔法使いのようにMacを使いこなせるようになっている筈だ。