読者です 読者をやめる 読者になる 読者になる

Hammerspoonでホットキーのキーリマップ

20170308 追記

現在は以下の方法で実施しています。
改:Hammerspoonでホットキーのキーリマップ(blacklist方式) - rochefort’s blog

 

Hammerspoon が面白い - rochefort’s blog
上記で実施していた方法だとアプリ単位で設定が必要でした。(filterに対象アプリを絞る機能もあるみたい)
Vim Binding を Dash アプリ以外でも利用しようと思ったので、アプリ名で絞れそうなhs.application.watcherを利用して見ました。

参考

そのものズバリです。最初にこれ見たらよかった。
Karabiner 使えない対策: Hammerspoon で macOS の修飾キーつきホットキーのキーリマップを実現する - Qiita

以前のコード

local function keyStroke(key, mods)
  mods = mods or {}
  return function()
    hs.eventtap.keyStroke(mods, key, 1000)
  end
end

local function disableAll(keySet)
  for k, v in pairs(keySet) do v:disable() end
end

local function enableAll(keySet)
  for k, v in pairs(keySet) do v:enable() end
end

local vimBinding = {
  hs.hotkey.new({'ctrl'}, 'J', keyStroke('down')),
  hs.hotkey.new({'ctrl'}, 'K', keyStroke('up'))
}

hs.window.filter.new('Dash')
  :subscribe(hs.window.filter.windowFocused, function() enableAll(vimBinding) end)
  :subscribe(hs.window.filter.windowUnfocused, function() disableAll(vimBinding) end)

修正後

local function keyStroke(key, mods)
  mods = mods or {}
  return function()
    hs.eventtap.keyStroke(mods, key, 1000)
  end
end

local function disableAll(keySet)
  for k, v in pairs(keySet) do v:disable() end
end

local function enableAll(keySet)
  for k, v in pairs(keySet) do v:enable() end
end

local function hasValue (tab, val)
  for index, value in ipairs(tab) do
    if value == val then
      return true
    end
  end
  return false
end

local vimBinding = {
  hs.hotkey.new({'ctrl'}, 'J', keyStroke('down')),
  hs.hotkey.new({'ctrl'}, 'K', keyStroke('up'))
}

local whiteListApps = {
  "Dash", "Evernote"
}

local function handleGlobalAppEvent(name, event, app)
  if event == hs.application.watcher.activated then
    if hasValue(whiteListApps, name) then
      enableAll(vimBinding)
    else
      disableAll(vimBinding)
    end
  end
end

appsWatcher = hs.application.watcher.new(handleGlobalAppEvent)
appsWatcher:start()

少し解説

hs.application.watcherを使って、vimBindingの切り替えを行なっています。
ここがLuaいけてないなぁと思うところですが、対象アプリをwhite listに持たせてハンドリングしています。
当初、正規表現で string.match(name, “Dash|Evernote”) みたいなことできるかと思ったのですが、 なんと標準の正規表現では| をサポートしていませんでした。

lua-users wiki: Patterns Tutorial

Limitations of Lua patterns

Especially if you're used to other languages with regular expressions, you might expect to be able to do stuff like this:

'(foo)+' -- match the string "foo" repeated one or more times
'(foo|bar)' -- match either the string "foo" or the string "bar"

tableにもvalueをsearchするfunctionが実装されていないということもあり、仕方なく都度捜査しています。