WSL の Emacs で日本語入力

## <i class="fa fa-paw fa-fw"></i>はじめに
サブのノートパソコンを買ったので，私の備忘録代わりのメモにします．使用する Linux は `Ubuntu 18.04` で，今回初めて `WSL` を使うので，`apt install emacs25` でインストールした `Emacs 25.2` になります．そのうち `Emacs 26.1` に移行したいと思っています． 

ほとんど（全部?）[NTEmacs @ ウィキ](https://www49.atwiki.jp/ntemacs/pages/69.html) の内容です．また，`mozc_emacs_helper` のパッチは MSYS2 の Emacs でもお世話になった，[rzl24ozi](https://github.com/rzl24ozi/mozc-emacs-helper-module) さんの `mozc_emacs_helper.cc.diff` を使いました．ありがとうございます．

## <i class="fa fa-leaf fa-fw"></i> mozc_emacs_helper
`mozc_emacs_helper` から `Google 日本語入力` を利用します．構築手順は，[How to build Mozc in Windows](https://github.com/google/mozc/blob/master/docs/build_mozc_in_windows.md)のままです．

### 下準備
* `Google 日本語入力` をインストールする．
* `Visual Studio Community 2015` をインストールする(デフォルトのままインストールすると `Microsoft Foundation Classes for C++` がインストールされないので，忘れずに選択する)

### depot_tools の準備
[depot_tools](https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html) から `depot_tools.zip` をダウンロードして展開する(ここでは，`C:\Programs` に展開)．

ここからは，コマンドプロンプトから作業します．

* `depot_tools` を `PATH` に追加

```cmd
> set PATH=C:\Programs\depot_tools;%PATH%;
```

* プロキシの設定

プロキシの設定が必要なら設定しておきます．私の環境なので，下記設定で繋がらない場合もあるかもしれません．

管理者権限でコマンドプロンプトを立ち上げる．

```
> netsh winhttp set proxy サーバ名:ポート番号
```

管理者権限のコマンドプロンプトは必要ないので，`depot_tools` の `PATH` を設定したコマンドプロンプトに戻ります．

```cmd
> set HTTP_PROXY=http://サーバ名:ポート番号
> set HTTPS_PROXY=http://サーバ名:ポート番号

> git config --global http.proxy http://サーバ名:ポート番号
> git config --global https.proxy http://サーバ名:ポート番号
```

* 必要なライブラリの取得
`gclient` を 2 回実行する．

```cmd
> gclient
> gclient
```

### mozc_emacs_helper の構築
ここでは，`C\Users\xxx\Downloads` での作業とします(xxx はユーザ名)．

* ソースコードの取得

```cmd
> cd \Users\xxx\Downloads
> mkdir work
> cd work
> git clone https://github.com/google/mozc.git -b master --single-branch --recursive
```

* パッチを当てる
* 
下記内容の `mozc_emacs_helper.patch` を `mozc` フォルダに作成する．

```cmd
> cd mozc
> patch -p0 < mozc_emacs_helper.patch
```

```diff:mozc_emacs_helper.patch
diff -crN src.org/unix/emacs/mozc_emacs_helper.cc src/unix/emacs/mozc_emacs_helper.cc
*** src.org/unix/emacs/mozc_emacs_helper.cc	2018-06-14 08:47:29.611920200 +0900
--- src/unix/emacs/mozc_emacs_helper.cc	2018-06-14 10:49:27.238940900 +0900
***************
*** 92,97 ****
--- 92,109 ----
      switch (command.input().type()) {
        case mozc::commands::Input::CREATE_SESSION:
          session_id = client_pool.CreateClient();
+ #ifdef OS_WIN
+         {
+           std::shared_ptr<mozc::client::Client> client =
+             client_pool.GetClient(session_id);
+           CHECK(client.get());
+           mozc::commands::KeyEvent key;
+           key.set_special_key(mozc::commands::KeyEvent::ON);
+           if (!client->SendKey(key, command.mutable_output())) {
+             ErrorExit(mozc::emacs::kErrSessionError, "Send IME-On key failed");
+           }
+         }
+ #endif // OS_WIN
          break;
        case mozc::commands::Input::DELETE_SESSION:
          client_pool.DeleteClient(session_id);
diff -crN src.org/win32/build32/build32.gyp src/win32/build32/build32.gyp
*** src.org/win32/build32/build32.gyp	2018-06-14 08:47:30.721733000 +0900
--- src/win32/build32/build32.gyp	2018-06-14 10:48:29.888967300 +0900
***************
*** 49,54 ****
--- 49,55 ----
              '../broker/broker.gyp:mozc_broker32',
              '../ime/ime.gyp:mozc_ime',
              '../tip/tip.gyp:mozc_tip32',
+             '../../unix/emacs/emacs.gyp:mozc_emacs_helper',
            ],
            'conditions': [
              ['branding=="GoogleJapaneseInput"', {
diff -crN src.org/win32/build64/build64.gyp src/win32/build64/build64.gyp
*** src.org/win32/build64/build64.gyp	2018-06-14 08:47:30.721733000 +0900
--- src/win32/build64/build64.gyp	2018-06-14 10:48:47.009940600 +0900
***************
*** 46,51 ****
--- 46,52 ----
              '../broker/broker.gyp:mozc_broker64',
              '../ime/ime.gyp:mozc_ime',
              '../tip/tip.gyp:mozc_tip64',
+             '../../unix/emacs/emacs.gyp:mozc_emacs_helper',
            ],
            'conditions': [
              ['branding=="GoogleJapaneseInput"', {
```

* ビルドする．

```cmd
> cd src
> python build_mozc.py gyp --noqt --branding=GoogleJapaneseInput
> python build_mozc.py build -c Release package
```

`src/out_win` の `Release` と `Release_x64` に `mozc_emacs_helper.exe` ができているのを確認します．私の環境は `64bit` なので，`Release_x64` の `mozc_emacs_helper.exe` を使います．

* 動作確認
* 
コマンドプロンプトでは，動作確認できないので，`WSL` の `bash` で確認する．

```bash
$ cd /mnt/c/Users/xxx/Downloads/work/mozc/src/out_win/Release_x64
$ echo -e '(0 CreateSession)\n(1 SendKey 1 97)' | ./mozc_emacs_helper.exe
```

下記のような出力がされると思います．

```bash
((mozc-emacs-helper . t)(version . "2.23.2815.100")(config . ((preedit-method . roman))))
((emacs-event-id . 0)(emacs-session-id . 1)(output . ((id . "4619524303035740612")(mode . hiragana)(consumed . t)(status . ((activated . t)(mode . hiragana)(comeback-mode . hiragana))))))((emacs-event-id . 1)(emacs-session-id . 1)(output . ((id . "4619524303035740612")(mode . hiragana)(consumed . t)(preedit . ((cursor . 1)(segment ((annotation . underline)(value . "あ")(value-length . 1)(key . "あ")))))(candidates . ((size . 1)(candidate ((index . 0)(value . "あ")(annotation . ((description . "ひらがな")))(id . 0)))(position . 0)(category . suggestion)(display-type . main)(footer . ((label . "Tabキーで選択")))(page-size . 9)))(status . ((activated . t)(mode . hiragana)(comeback-mode . hiragana)))(all-candidate-words . ((candidates ((id .0alue . "あ")(value-length . 1)(key . "あ")))))(candidates . ((size . 1)(candidate ((i)(index . 0)(value . "あ")(annotation . ((description . "ひらがな")))))(category . suggestion))))))
```

## Emacs
`Emacs` での設定を行います．

### 下準備
Windows の `exe` は，`DrvFS(Windows のファイルシステム?)` 上にないといけないようですので、少し小細工をします．

%USERPROFILE%（C:\Users\xxx） に `WSL` の名前でフォルダを作ります．

`bash` 上で作業．

```bash
$ ln -s /mnt/c/User/xxx/WSL ~/wsl
$ mkdir -p ~/wsl/bin
```

`C:\Users\xxx\WSL\bin` に `mozc_emacs_helper.exe` をコピーする．

`$HOME/wsl/bin` に `Path` を通す．`.bashrc` などに追加．

`bash` 上で作業．

```bash:.bashrc
if [ -d "$HOME/wsl/bin" ] ; then
     PATH="$HOME/wsl/bin:$PATH"
fi
```

`$HOME/wsl/bin` に `mozc_emacs_helper.sh` を作り，実行権限を与える('DrvFS' 上なので パーミッションは．`777` から変更できないので，実行権限は与えられてるはず)．

```bash:mozc_emacs_helper.sh
#!/bin/sh

mozc_emacs_helper.exe "$@" 2> /dev/null
```

### 設定
`~/.emacs.d/init.el` に `mozc_emacs_helper` の設定を追加します．'use-packge' を使っているのでその書き方になっています．
また，`https://github.com/iRi-E/mozc-el-extensions` の `mozc-cursor-color.el` を `~/emacs.d/lisp` に置いています．他の elisp は，`use-package` がインストールしてくれます．プロキシ環境の方は別途設定がいります（以前の記事にあったと思います）．

```lisp:init.el
;; NTEmacs @ ウィキ
;; https://www49.atwiki.jp/ntemacs/pages/48.html
(defvar-local mozc-im-mode nil)

(use-package mozc
  :ensure t
  :init
  (setq mozc-helper-program-name "mozc_emacs_helper.sh")
  :custom
  (mozc-leim-title "[も]"))

(use-package mozc-im
  :ensure t
  :after mozc
  :init
  (add-hook 'mozc-im-activate-hook (lambda () (setq mozc-im-mode t)))
  (add-hook 'mozc-im-deactivate-hook (lambda () (setq mozc-im-mode nil)))
  :config
  (setq default-input-method "japanese-mozc-im")
  (add-hook 'isearch-mode-hook (lambda () (setq im-state mozc-im-mode)))
  (add-hook 'isearch-mode-end-hook
            (lambda ()
              (unless (eq im-state mozc-im-mode)
                (if im-state
                    (activate-input-method default-input-method)
                  (deactivate-input-method)))))
  )

(use-package mozc-popup
  :if window-system
  :ensure t
  :config
  (setq mozc-candidate-style 'popup))

;; https://github.com/iRi-E/mozc-el-extensions
;; `mozc-cursor-color.el` を `lisp` に
(use-package mozc-cursor-color
  :after mozc-im
  :load-path "lisp/"
  :config
  (setq mozc-cursor-color-alist
        '((direct . "#ccccc7")
          (read-only . "#50fa7b")
          (hiragana . "#b45bcf")
          (full-katakana . "goldenrod")
          (half-ascii . "dark orchid")
          (full-ascii . "orchid")
          (half-katakana . "dark goldenrod")))

  (advice-add 'mozc-cursor-color-update :around
              (lambda (orig-fun &rest args)
                (let ((mozc-mode mozc-im-mode))
                  (apply orig-fun args))))
  )

```

`C-\` で日本語入力ができると思います．`Emacs` を立ち上げて初回の変換だけ少し注意が必要です．
ドラッグでファイルが開けないのが少し不便ですが，`MSYS2` の `Emacs` より快適かもしれません．

### `Migemo` 関連の設定
取り敢えず，`apt install cmigemo` でインストールして，`init.el` などに下記設定を追加する．

```lisp:init.el
;;; avy
(use-package avy
  :custom
  (avy-timeout-seconds 1.0) ;migemo 対応のため 0.5 から変更
  :config
  (avy-setup-default))

;;; ivy/counsel
(use-package ivy
  :ensure t
  :pin melpa
  :diminish
  :demand t
  :custom
  (ivy-height 10)
  (ivy-use-virtual-buffers t)
  (ivy-count-format "(%d/%d) ")
  :config
  (ivy-mode 1))

;;; bind-key C-o
(use-package ivy-hydra
  :ensure t
  :pin melpa
  :after (ivy hydra)
  :defer t)

;;; counsel
(use-package counsel
  :ensure t
  :pin melpa
  :after ivy
  :demand t
  :diminish)

;;; swiper
(use-package swiper
  :after ivy
  :bind (:map isearch-mode-map
              ("C-o" . swiper-from-isearch)))

;;; migemo
(use-package migemo
;  :disabled t
  :ensure t
  :if (executable-find "cmigemo")
  :config
  (setq migemo-command "cmigemo"
        migemo-options '("-q" "--emacs")
        migemo-dictionary "/usr/share/cmigemo/utf-8/migemo-dict"
        migemo-user-dictionary nil
        migemo-regex-dictionary nil
        migemo-coding-system 'utf-8-unix)
  (migemo-init))

;;; anzu
(use-package anzu
  :ensure t
  :defer t
  :bind
  (([remap query-replace] . anzu-query-replace)
   ([remap query-replace-regexp] . anzu-query-replace-regexp)
   :map isearch-mode-map
   ([remap isearch-query-replace] . anzu-isearch-query-replace)
   ([remap isearch-query-replace-regexp] . anzu-isearch-query-replace-regexp))
  :init (global-anzu-mode 1)
  :config
  (set-face-attribute 'anzu-mode-line nil
                      :foreground "#f1fa8c" :weight 'normal)
  (setq anzu-search-threshold 1000
        anzu-cons-mode-line-p nil
        anzu-use-migemo t)
  :diminish anzu-mode)

;;; avy-migemo
(use-package avy-migemo
  :ensure t
  :after (avy migemo swiper)
  :defer t
  :bind* ("C-." . avy-migemo-goto-char-timer)
  :config
  (avy-migemo-mode 1)
  (require 'avy-migemo-e.g.swiper))
```

* `C-.` で `avy` でカーソルを移動．
* `C-s` で `isearch` 後，`C-o` で `swiper` を起動．