Slack の入力時に Enter キーで改行したい

# Slack の入力時に Enter キーで改行したい

## はじめに

> このエントリーは [KLab Advent Calendar 2016](http://qiita.com/advent-calendar/2016/klab) の 7 日目の記事です。

皆さん、業務でのチャットツールは何をお使いでしょうか。おなじみの [Slack](https://slack.com/) や [ChatWork](http://www.chatwork.com/ja/) 、 IRC のところもあるでしょう。
KLab 株式会社では全社的に Slack を利用しており、 API が充実していて開発者としては非常に使いやすいのですが、 1 点不満点があります。
それは、「Enter キーを押すと送信してしまう」ことです。

様々なチャット / メッセージングサービスにおいて Enter キーを改行 or 送信のどちらとして扱うかはバラバラで統一されていません。
慣れの問題と言われてしまえばそれまでなのですが、使うサービスを頻繁に切り替えているとうっかり誤爆してしまいやすく困りますよね。

設定の Preferences → Advanced に

> When typing code with \```, Enter should not send the message

という、先頭に「``` (コードブロック)」を入力していると Enter で改行、 Shitf + Enter で送信になる設定項目があるのですが、求めているのは普通の文章を入力中にも Enter で改行するオプションです。
このオプションは Line や ChatWork には実装されていますが、Slack にはありません。Twitter の Direct Message や [Discord](https://discordapp.com/) にもなかったので、2 バイト文字圏との文化的な違いもあるのでしょうか？

というわけで、ないものは作りましょう。

## Slack.app 編

Slack には[デスクトップ版アプリケーション](https://slack.com/downloads/) (Windows / macOS / Linux) が存在し、それがアクティブな場合のみ Enter を別の入力にしてしまえばよいと考え、 Mac の Slack.app 向けに [Karabiner](https://pqrs.org/osx/karabiner/index.html.ja) の設定ファイルを作ることにしました。

### 導入方法

1. [Karabiner](https://pqrs.org/osx/karabiner/index.html.ja) をインストール
1. `~/Library/Application Support/Karabiner/private.xml` の `<root>` 〜 `</root>` 内に下記[設定内容](#設定内容)を追記
1. Karabiner の設定画面を開き、 Reload XML を押す
1. remapping 一覧に「For Slack」ができているので開き、好きなものを選択 (両方選択可能)
    - Enter で改行、⌘ (Command) + Enter で送信
    - Enter で改行、Shift + Enter で送信
1. (以下、 Google 日本語入力を使っている場合) Google 日本語入力の環境設定を開く
1. 「一般」→「キー設定」→「キー設定の選択」→「編集…」で一覧が開くので、「編集」→「エントリーを追加」で下記のキー設定を追加する

| モード       | 入力キー     | コマンド |
|--------------|--------------|----------|
| 変換前入力中 | Option Enter | 確定     |
| 変換中       | Option Enter | 確定     |

#### 設定内容

```XML
<appdef>
    <appname>SLACK</appname>
    <equal>com.tinyspeck.slackmacgap</equal>
</appdef>
<item>
    <name>For Slack</name>
    <item>
        <name>Use Enter to create a new line and ⌘ + Enter to post</name>
        <identifier>private.app_slack_command_enter_to_post</identifier>
        <only>SLACK</only>
        <autogen>
            __KeyToKey__
            KeyCode::RETURN, ModifierFlag::NONE,
            KeyCode::RETURN, ModifierFlag::OPTION_R, ModifierFlag::FN
        </autogen>
        <autogen>
            __KeyToKey__
            KeyCode::RETURN, ModifierFlag::COMMAND_R | ModifierFlag::NONE,
            KeyCode::RETURN, ModifierFlag::NONE
        </autogen>
    </item>
    <item>
        <name>Use Enter to create a new line and Shift + Enter to post</name>
        <identifier>private.app_slack_shift_enter_to_post</identifier>
        <only>SLACK</only>
        <autogen>
            __KeyToKey__
            KeyCode::RETURN, ModifierFlag::NONE,
            KeyCode::RETURN, ModifierFlag::OPTION_R, ModifierFlag::FN
        </autogen>
        <autogen>
            __KeyToKey__
            KeyCode::RETURN, ModifierFlag::SHIFT_R | ModifierFlag::NONE,
            KeyCode::RETURN, ModifierFlag::NONE
        </autogen>
    </item>
</item>
```

### 仕組み

- 修飾キーなしの通常 Enter キー入力 (`KeyCode::RETURN, ModifierFlag::NONE`) を、適当な修飾キーと組み合わせて Slack における改行にあたり、且つ他のショートカットとあまり重複しないキーバインド (今回は Enter + 右 Option + Fn) にしています
  - そのキーバインドで通常の Enter と同様の操作が行えるように IME 側の設定を追加します
- 送信に使いたい修飾キー と Enter キーの同時入力 (`KeyCode::RETURN, ModifierFlag::SHIFT_R | ModifierFlag::NONE`) を Enter キー入力にしています

### 注意点

この方法は IME 側の設定をしないと、 Enter キーで変換を確定できなくなってしまいます。
Google IME 以外では使っている IME のキーバインドと衝突してしまうかもしれません。
その場合は設定内容の `ModifierFlag::OPTION_R`, `ModifierFlag::FN` を他のキーに置き換えるなどして下さい。

## Google Chrome 編

前述の Slack.app 向け Karabinar 設定を導入した直後、社内でも同様の悩みを抱えている人が複数いることを知りました。
そのため、より多くの人が Enter キーで改行できるように Chrome Extension を作成することにしました。

### 実装方法

#### 書き換えたい event handler を探す

Chrome の Developer Tool を使い、対象となりそうな Element の Event Listener を取得します。

<img width="318" alt="スクリーンショット 2016-12-05 0.37.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/21581/056e89c8-b473-0008-ef42-6ffa18487b91.png">

今回の場合 keydown に紐付いていそうですね。上から虱潰しに調べてきます。

#### event handler を書き換える

それらしき処理が記述されている handler を見つけたら、それを望む形に書き換えます。
今回は Enter キー単体の入力と Enter キー + 修飾キー同時入力 の扱いが逆になるようにします。

#### 既存の handler を unbind し、新たに bind する

```JavaScript
var input = $('#message-input');
var handlers = $._data(input.get(0), "events").keydown;

input.unbind("keydown", handlers[handlers.length - 1]);
input.bind("keydown", function(e) {...})
```

ここで注意したいのは、 handler が登録されている順番は確定できないということです。
今回は比較的うまくいきましたが、サービスによっては JavaScript がロードされる順番などが変動し安定して動作しない恐れがあります。

#### Chrome Extension にする

Chrome Extension の Content Script 内で、書き換えた handler と `unbind()`, `bind()` の一連のコードを `script` 要素としてページに追加してしまいます。
この手法については [Chrome Extension のcontent scriptでの変数隔離に対応する。 - それマグで！](http://takuya-1st.hatenablog.jp/entry/2014/11/04/001449) に詳しい解説があります。

### 成果物

Slack Enter Key Modifier ([Chrome ウェブストア](https://chrome.google.com/webstore/detail/slack-enter-key-modifier/ejlmecppipaakakoclcfmlnheifdngeg/))

インストールすると、メッセージ入力欄の右側に「Enter キーで送信」というチェックボックスができます。

<img width="175" alt="スクリーンショット 2016-12-05 17.48.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/21581/9b26c30b-a9f7-2310-9774-37826d984a37.png">

これにより、 Enter キーの挙動を簡単に切り替えられます。
冒頭の

> When typing code with \```, Enter should not send the message

は OFF にしておきましょう。

## おわりに

なんとしても Enter キーで改行したいという目的は果たすことができました。
しかしながら、こういったやり方はサービス本体のコードの変更に弱い上、ライセンスの問題にも絡んでくるため、あまり良いとは言えません。

そのような心配なく、各サービスで Enter キーの扱いを当たり前に変更できる未来を望みます。

### 参考

- [Karabiner の private.xml 設定方法 - Qiita](http://qiita.com/altitude3190/items/bbef986ff8dd288b2641)
- [Slackのチャンネル移動をキーボードで簡単に出来るようKarabinerのprivate.xmlを書いた - Glide Note - グライドノート](http://blog.glidenote.com/blog/2014/08/06/custom-keybind-for-slack/)
- [Chrome Extension のcontent scriptでの変数隔離に対応する。 - それマグで！](http://takuya-1st.hatenablog.jp/entry/2014/11/04/001449)
- [jQuery find events handlers registered with an object - Stack Overflow](http://stackoverflow.com/questions/2518421/jquery-find-events-handlers-registered-with-an-object)