ChromeOSでEmacs Keybindを使う

ChromeOS上のchromeでemacsキーバインドを実現するextensionかつIMEをつくりました。興味ある方いたら使ってみてください。使い方は結構ややこしいです。
私はDevチャンネルで使っているのでそれ以外の環境で使ってみてくれる人いたらうれしいです。

## できること

ChromeOS上のChromeのテキスト入力可能なところでのEmacs Likeなキー操作

### サポートしているキーバインド

* **Ctrl+P** (Up)
* **Ctrl+N** (Down)
* **Ctrl+B** (Left)
* **Ctrl+F** (Right)
* **Ctrl+A** (Home)
* **Ctrl+E** (End)
* **Ctrl+D** (Delete)
* **Ctrl+H** (Backspace)
* **Ctrl+M** (Enter)
* **Ctrl+K** (Kill line to end)
* **Ctrl+W** (Cut)
* **Ctrl+Y** (Paste)

## Extensionのインストール

https://chrome.google.com/webstore/detail/emacs-shortcuts-ime/mkegpdcnenfnhenaliggngipehndddne

## 使い方

こちらを熟読ください。

https://github.com/OTL/emacs_shortcuts_ime

主に以下のような手順が必要です。
(画像はdevチャンネルで作ったのでstableとは違います。)

* [Extensionのインストール](https://chrome.google.com/webstore/detail/emacs-shortcuts-ime/mkegpdcnenfnhenaliggngipehndddne
)
* 手動でのshortcutの設定 (chrome://extensions/shortcuts)
![screen_shortcuts.png](https://qiita-image-store.s3.amazonaws.com/0/58926/473acab5-db4a-96b3-8c46-403534ea7b9b.png)
* IMEの有効化
![screen1.png](https://qiita-image-store.s3.amazonaws.com/0/58926/a24b3884-86da-271a-4f15-19cc17677047.png)
![screen2.png](https://qiita-image-store.s3.amazonaws.com/0/58926/80681ada-4d22-6014-23d5-b1e32e7a1086.png)
* IMEの選択
![screen3.png](https://qiita-image-store.s3.amazonaws.com/0/58926/b43a402d-1693-1e5b-4dd8-0734d30005e7.png)

ここまでやって、Google Docsやgmailなどで試して見てください。

## 作るまでの顛末

Chromeとemacsキーバインドはけっこう相性が悪くて、ChromeのGoogle Docs上でemacs keybindを使えるのはMacOSくらい。
MacBookからPixelbookに乗り換えようと意気込んていたが、Emacsキーバインドが快適に使えるMacBookと比べると非常に使いづらい。

調べてみるといくつかツールがあるけど、Ctrl+PやCtrl+Nなど重要なバインドがChromeネイティブのショートカットとして拾われてしまいうまく動かない。

こういう記事も見つけたけど、私の環境ではうまく動かず。

https://note.mu/junkawashima/n/na18827f2231f

私の目的としてはGoogle Docsが快適に操作できることが最優先。
Crostiniで動くLinuxアプリは大体Emacsキーバインド使えるので、Chromeさえなんとかなればよかった。

上記記事で紹介されている

https://github.com/ento/chromeos-key-remapper

をいじっているうちに、うまく行く方法を発見してゼロから作ることになった。chrome extensionもはじめて作った。

あと夏休みで時間があった。

## 実現方法

ChromeOSとChromeの制限をいかにかいくぐるか、という勝負。

### `chrome.commands` を使ってショートカットの上書き

Emacsキーバインドで重要なCtrl+P, Ctrl+N, Ctrl+FなどはChromeがショートカットを使っているため、普通のツールでは上書きできない。
私が発見したのは`chrome.commands`というextensionのAPIを使って、それにショートカットを設定するとChromeのショートカットを上書きできることだった。
ただ、ひとつのextensionは4つまでしかデフォルトのショートカットを設定できない。この制限はchromeのソースコードにベタがきされており変更不可能。
また、先述のChromeが使っている、Ctrl+P, N, Fなどはデフォルトでは登録できない。そこで手動で設定してもらうことにした。
`manifest.json` に以下のような感じで書いておくと、

```json
{
  "commands": {
    "00-up": {
      "description": "Go Up (Ctrl+P)"
    },
    "01-down": {
      "description": "Go Down (Ctrl+N)"
    }
}
```

以下のような感じでjsで処理できる。

```javascript
chrome.commands.onCommand.addListener(function (c) {
  switch (c) {
    case "00-up":
      sendEvent('ArrowUp');
      break;
    case "01-down":
      sendEvent('ArrowDown');
      break;
   ....
}
```

コマンド名に数字がついているのはショートカット設定ページで名前の順で勝手にソートされるので、任意の順位するため。

### `chrome.input.ime` による入力処理

よく調べていないが、Chromeでキー入力をするにはIMEのAPIを使うしかないようだ。なのでIMEとして作ることになった。IMEを作るというとすごく大変そうだが、実際にはコードを１行も書かなくてもIMEにすることができる。IMEにするとテキストボックスなど文字入力が可能な状態でキーコードを送ることができる。
コードは以下のような感じ。

```javascript
chrome.input.ime.sendKeyEvents({ "contextID": contextId, "keyData": [keyData] });
```

私が作ったextensionではこの`chrome.input.ime.sendKeyEvents`を使って仮想的にキーを入力することで各種ショートカットを実現している。
例えば`Ctrl+K`は`Shift + End`して、`Ctrl+X` (切り取り)することで実現している。これをショートカットからの`chrome.commands`という普通IMEで使わないようなところから呼び出すことでemacs keybindを実現している。

manifest.jsonとmain.jsで合わせても200行もなく実現出来た。


## 出来ないこと

* 今のところChrome上ででしか動かない。ChromeOS搭載の謎のテキストエディタなどでは動かない。`chrome.commands`にはglobalなショートカットのオプションがあるがChromeOSでは動かない。
* `Ctrl+Space`など単純なキー入力で実現出来ないものはサポートしていない。がんばればできるかもだけど、`Ctrl+Space`は特にそもそもIME切り替えなので難しいだろう。
* また、潰してしまったCtrl+Fなどのショートカットが使えない。必死で潰したのにないと今度は困る。これはなんとかしたいところだがやり方がわからない。
* 日本語入力中に使えない。これはMozcを改造して自分でビルドすれば多分できる。Google Docsだと`Ctrl+Shift+K`で日本語入力できるので取り敢えずやらない。Qiitaを書くのに早速今困っているがなんとかなる。