13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Hammerspoonで macOS Sierra + Xcode に emacs ライクなキーバインドを設定した

Last updated at Posted at 2017-01-18

macOS Sierra になってから、これまで使えていた Karabiner が使えなくて非常に困った。
もともと Karabiner は Xcode で emacs ライクなキーバインドを実現するために使用していたが、
代替として Hammerspoon に移行できたのでその設定内容を記載。

動作確認環境

  • macOS Sierra 10.12.2
  • Hammerspoon 0.9.48

主な設定内容

  • C-x C-s で上書き保存(⌘+s)
  • C-x C-f で Open Quickly (⌘+shift+o)
  • C-space で set-mark 的なもの
  • 設定は Xcode 操作時のみ有効

設定ファイル

Hammerspoon の導入などは省略します。
設定ファイルの全文は Github で管理しているのでそちらを参照。

ちなみに、今は使っていませんが Karabinerの設定ファイルも同じリポジトリにあります。

設定内容をかいつまんで記載します。

C-x C-s で上書き保存(⌘+s)

C-x トリガで commandMode:enable() 関数を呼び出し、

-- command mode
hs.hotkey.new({'ctrl'}, 'x', function() commandMode:enable() end),

関数内で hs.eventtap.new にてキーダウンイベントを監視します。
登録した 2ストローク目のキー(例えば C-s)と一致した場合に該当する関数(例えば ⌘-sを発行)を呼び出します。

obj.enable = function(self)
    info(self.name.." start")
    self.commandWatcher:start()
end
obj.commandWatcher = hs.eventtap.new( {hs.eventtap.event.types.keyDown},
    function(tapEvent)
        for k,v in pairs(obj.commandTable) do
            if v.key == hs.keycodes.map[tapEvent:getKeyCode()] and isEqualTable(v.modifiers, tapEvent:getFlags()) then
                info(obj.name.." end")
                obj.commandWatcher:stop()
                if v.func then
                    v.func()
                end
                return true
            end
        end

        if obj.othersFunc then
            return othersFunc(tapEvent)
        end
    end)
return obj

このあたりの書き方は下記のページなどを参照させていただき、クラスっぽい記載にしました。
http://www.ie.u-ryukyu.ac.jp/~e085739/lua.hajime.4.html

C-space で set-mark 的なもの

上記の C-x C-s の場合と同じような記載にしましたが、
登録されている 2ストローク目のキー以外のキーが押された場合に shift を強制的に押されたような動作をするようにしました。
これにより、C-space C-f C-f などと押したときに、shift を押しながらカーソル移動したのと同じような動作をさせるようにしています。

function(tapEvent) -- force shift on
    flags = tapEvent:getFlags()
    flags.shift = true
    tapEvent:setFlags(flags)
    return false
end

ちなみにXcode でもデフォルトで C-@ でset mark できますが、選択範囲をわかりやすくしたかったのと、OSのクリップボードと共有させたかったので上記のような設定をしています。

設定は Xcode 操作時のみ有効

下記コードにより ウィンドウの切り替えを監視し、Xcode フォーカス時にのみ設定が有効になるようにしています。

hs.window.filter.new('Xcode')
    :subscribe(hs.window.filter.windowFocused,function() enableAll(xcodeBindings) end)
    :subscribe(hs.window.filter.windowUnfocused,function()
        disableAll(xcodeBindings)
        markMode:disable()
        commandMode:disable()
    end)

最後に

Xcode だけで emacs キーバインドができれば全て解決ですが、それまではこれでしのぎます。
API仕様を見た感じHammerspoon はもっといろいろなことができそうなので、今後も触っていこうかと思います。
http://www.hammerspoon.org/docs/

13
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?