HammerspoonでEvernoteの行入れ替えを行う

20170319 編集

lineMoveUpDownBinding 変数の中で hs.hotkey.bind していたのを hs.hotkey.newに変更。
bindだとhotkeyが即有効になるため、Evernote以外で動作する可能性がありました。


Macのエディターには行の入れ替えがショートカットがついていたりします。
(Cmt + Ctrl + ↑/↓)
CotEditor、Atomにはあり、よく使っています。
(確かtextemateやsublimeも確かありましたよね)
Evernoteのcheckボタン付きリストをなんとかこんな感じでできないかやってみました。

やってみた

きっとこの使い方は間違っていますが、とりあえず動くので使ってみようと思います。(より良い方法があれば是非知りたいです。)
delayの時間が短すぎると、ちゃんと動かなかったりするので一部多めに設定しています。

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 delay = 10000
local lateDelay = 100000
local function moveLineDown()
  return function()
    hs.eventtap.keyStroke({'cmd'}, 'right', delay)
    hs.eventtap.keyStroke({}, 'right', delay)
    hs.eventtap.keyStroke({}, 'delete', delay)
    hs.eventtap.keyStroke({'shift', 'cmd'}, 'left', lateDelay)
    hs.eventtap.keyStroke({'cmd',}, 'X', lateDelay)
    hs.eventtap.keyStroke({}, 'down', delay)
    hs.eventtap.keyStroke({'cmd'}, 'V', delay)
    hs.eventtap.keyStroke({}, 'return', delay)
    hs.eventtap.keyStroke({}, 'delete', delay)
    hs.eventtap.keyStroke({}, 'left', delay)
  end
end

local function moveLineUp()
  return function()
    hs.eventtap.keyStroke({'cmd'}, 'left', delay)
    hs.eventtap.keyStroke({}, 'delete', delay)
    hs.eventtap.keyStroke({'shift', 'cmd'}, 'left', lateDelay)
    hs.eventtap.keyStroke({'cmd',}, 'X', lateDelay)
    hs.eventtap.keyStroke({}, 'down', delay)
    hs.eventtap.keyStroke({'cmd'}, 'V', delay)
    hs.eventtap.keyStroke({}, 'return', delay)
    hs.eventtap.keyStroke({}, 'delete', delay)
    hs.eventtap.keyStroke({}, 'up', delay)
    hs.eventtap.keyStroke({}, 'left', delay)
  end
end

local lineMoveUpDownBinding = {
  hs.hotkey.new({'cmd', 'ctrl'}, 'down', moveLineDown()),
  hs.hotkey.new({'cmd', 'ctrl'}, 'up', moveLineUp())
}

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

こんな感じ

動きがかなり気持ち悪いです。。
f:id:rochefort:20170307000521g:plain

X-Recruitment header

Webエンジニアに知ってほしいRDBアンチパターン // Speaker Deck を読んでいたら、以下のコマンドでリクルートしてる旨のページがあった。

$ curl -sIL mackerel.io | grep engineer
X-Recruitment: http://hatenacorp.jp/recruit/career/operation-engineer
X-Recruitment: http://hatenacorp.jp/recruit/career/sales-engineer
X-Recruitment: http://hatenacorp.jp/recruit/career/application-engineer

 
http headerにX-Recruitmentってのを突っ込んでいるみたい。
htmlのコメントでリクルートしてるのを見つけたら嬉しい気持ちになるが、http headerでやるとは。。。
若干中二っぽい印象。
X-Recruitment ってのは一般的なのかとググってみたが、あまりヒットせず。
4 Creative Ways to Recruit Web Developers

改:Hammerspoonでホットキーのキーリマップ(blacklist方式)

Spotlight、Alfred でも Vim Binding やりたくなってきたので、少し修正しました。
昨日書いたやり方ホワイトリスト方式ですので ここにアプリ名追加すれば良いかと思ったら、はまってしまいました。
 
hammerspoon/filter.lua at 3e141946732a66547dd9e43d31fd2008a68c020a · Hammerspoon/hammerspoon
に記載のある通り、SKIP_APPS_TRANSIENT_WINDOWS で対象外アプリを設定しているようです。
原因っぽいのはわかりましたが、いまいち回避方法がわからない。。

ブラックリスト方式

hs.window.filter.default:allowApp'Spotlight' みたいにやれと書いてありますが、どうもうまくいかないのでブラックリスト方式でやってみました。
Editor、iterm、chrome を対象外にする方法です。

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.bind({'ctrl'}, 'J', keyStroke('down')),
  hs.hotkey.bind({'ctrl'}, 'K', keyStroke('up'))
}

local notVimBindingApps = {
  "Atom", "CotEditor", "iTerm", "Google Chrome"
}

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

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