Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?
@y-sakata

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

More than 3 years have passed since last update.

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/

8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
8
Help us understand the problem. What is going on with this article?