MacOS Sierra で入力ソースの切り替えのキーバインドを設定する（HammerSpoon）

## はじめに
MacOS Sierra ではキーバインド変更アプリのKarabinerが利用できない。（Karabiner-Elementsのみ）
よってこれまでKarabinerで実現していた以下の機能を実現するアプリとしてHammerSpoon（ http://www.hammerspoon.org/ ）を利用する。

* SHIFT+SPACEによる日本語入力切替
* ESC押下に英数押下のアクション追加（vimのエディットモードをESCで抜ける際に日本語入力を解除するため）

Sierraにアップデートする前に「El Capitan」での動作確認を行なっているためこのバージョン以降であれば動作する。

## 環境情報
OS: 10.12.6 (MacOS Sierra)
HammerSpoon: 0.9.55

## HammerSpoon設定

### 設定作成/反映
メニューバーのアイコンを押下し、「Open Config」を選択すると設定ファイルのinit.luaがエディタで開かれる。

init.luaにluaで設定（コード）を記述し、再度メニューバーのアイコンから「Reload Config」を選択し設定を反映させる。

メニュバーから「Console」選択すると、設定読み込みの様子やエラーの確認ができるコンソールが開く。
コンソールの下部にはプロンプトも用意されているので、コードの断片のデバッグなどに利用できる。

なおメニューバーの「Preference」を選択し表示される設定画面下部の「Accessibility」が「Disable」だと利用できる機能に制限が発生する。（例：hs.eventtap.event.types.keyDown/keyUp）
特に理由がなければ「Enable」にする。そして「Enable」にした後、必ずHammerSpoonを再起動すること。

### init.lua
「hs」で始まる変数でHammerSpoonのAPIが利用できる。
APIの詳細は[ここから](http://www.hammerspoon.org/docs/index.html)。


```lua:init.lua
-----------------------------------------------------------------------------------
-- define const vars
-----------------------------------------------------------------------------------
---------------------
-- modifire keys
---------------------
--local MOD_COMMAND        =  55 --hs.keycodes.map['cmd']
--local MOD_SHIFT          =  56 --hs.keycodes.map['shift']
--local MOD_ALT            =  58 --hs.keycodes.map['alt']
--local MOD_CTRL           =  59 --hs.keycodes.map['ctrl']
---------------------
-- general keys
---------------------
local SPACE              =  49 --hs.keycodes.map['space']
local ESCAPE             =  53 --hs.keycodes.map['escape']
local KEY_CODE_JIS_EISUU = 102
--local KEY_CODE_JIS_KANA  = 104
---------------------
-- application names
---------------------
--local VIM_BINDING_APPS = {
--  "ターミナル", "iTerm2", "Boostnote"
--}

-----------------------------------------------------------------------------------
-- define functions
-----------------------------------------------------------------------------------
--------------------------------------------------------
-- constract function of  executing multiple functions.
--------------------------------------------------------
local function multiFunc(functions)
   return function()
      for i, func in ipairs(functions) do
         func()
      end
   end
end

--------------------------------------------------------
-- constract tap (press and release) key function. 
--------------------------------------------------------
local function tapKey(mods, key)
   mods = mods or {}
   return function()
      hs.eventtap.event.newKeyEvent(mods, key, true):post()
      hs.timer.usleep(1000)
      hs.eventtap.event.newKeyEvent(mods, key , false):post()      
   end
end

--------------------------------------------------------
-- set hotkey. (remap)
--------------------------------------------------------
local function remapKey(mods, key, tapKeyFunc)
   hs.hotkey.bind(mods, key, tapKeyFunc, nil, tapKeyFunc)
end

--------------------------------------------------------
-- handle key action event.
--------------------------------------------------------
local function keyactionEventHandler(e)
   local keycode = e:getKeyCode()
   --hs.alert(keycode)          
   if keycode == ESCAPE then
     hs.eventtap.event.newKeyEvent({}, KEY_CODE_JIS_EISUU, true):post()
   end
end

-----------------------------------------------------------------------------------
-- remap keys
-----------------------------------------------------------------------------------
--global
remapKey({'shift'}, SPACE, tapKey({'cmd'}, SPACE))

--for vim editor.  
--remapKey({}, ESCAPE, multiFunc({
--    tapKey({}, ESCAPE),
--    tapKey({}, KEY_CODE_JIS_EISUU)
--}))

-----------------------------------------------------------------------------------
-- listen event
-----------------------------------------------------------------------------------
local keyactionEventListener = hs.eventtap.new({
                          hs.eventtap.event.types.flagsChanged,
                          hs.eventtap.event.types.keyDown,
                          hs.eventtap.event.types.keyUp
                      }, keyactionEventHandler)
keyactionEventListener:start()
```

### 設定概要
日本語入力切り替えはホットキーで、ESC押下時の動作はイベントハンドリングにより実現。
（現状は利用していない設定や関数も含んでいるが、今後のためにある程度事前に用意。）

ESC押下時の動作をホットキーで実現すると、ESC押下してから反応するまでに1秒程度のタイムラグが発生するため不採用。
（複数のアクションを実行する参考例としてコメントアウト。）

```
--for vim editor.  
--remapKey({}, ESCAPE, multiFunc({
--    tapKey({}, ESCAPE),
--    tapKey({}, KEY_CODE_JIS_EISUU)
--}))
```

ホットキーの設定はremapKey関数で行う。remapKey関数の第1、第2引数で指定したホットキー押下時に、第3引数で指定した関数を実行する。
第3引数で利用しているtapKey関数は指定したキーの「押す」「離す」を1[msec]間隔で行う関数を返却する。

multiFunc関数を利用すれば、複数のアクションを1つのホットキーに割り当てることができる。（現状は利用していない）

キーに関するイベントの監視はkeyactionEventListenerで行なう。
hs.eventtap.event.typesの後に続くものが定義されたイベント。

* 修飾キーのイベント： flagsChanged
* 一般キーのイベント： keyDown/keyUp

修飾キー（modifires）のイベントはkeyDown/keyUpでは取得できない為、注意が必要。

## 参考
http://qiita.com/naoya@github/items/81027083aeb70b309c14
http://mizoguche.info/2017/01/hammerspoon_for_sierra/
http://kitak.hatenablog.jp/entry/2016/11/28/104038
http://rochefort.hatenablog.com/entry/2017/02/25/070000
http://virtnote.blogspot.jp/2017/04/hammerspoon.html