Create a gist now

Instantly share code, notes, and snippets.

@edosha /init.lua Secret
Last active May 1, 2017

What would you like to do?
Hammerspoon を使って Xcode で Emacs キーバインドを使うためのスクリプト
JIS_LEFT_BRACKET_CODE = 33
JIS_RIGHT_BRACKET_CODE = 30
local function info(message)
-- 各種デバッグメッセージ表示用
-- hs.alert.show(message)
end
local function getTableLength(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
local function isEqualTable(t1, t2)
if getTableLength(t1) ~= getTableLength(t2) then return false end
for k, v in pairs(t1) do
if t2[k] ~= v then
return false
end
end
return true
end
local function disableAll(keySet)
-- Xcode からフォーカスが外れたら、全部のキーマップを無効にする
for k, v in pairs(keySet) do v:disable() end
end
local function enableAll(keySet)
-- Xcode にフォーカスがあたったら、全部のキーマップを有効にする
for k, v in pairs(keySet) do v:enable() end
end
local function pressKey(modifiers, key)
modifiers = modifiers or {}
hs.eventtap.keyStroke(modifiers, key, 20 * 1000)
end
local function pressKeyFunc(modifiers, key)
return function() pressKey(modifiers, key) end
end
local function createKeyRemap(srcModifiers, srcKey, dstModifiers, dstKey)
dstFunc = function() pressKey(dstModifiers, dstKey) end
return hs.hotkey.new(srcModifiers, srcKey, dstFunc, nil, dstFunc)
end
SubMode = {}
SubMode.new = function(name, commandTable, othersFunc)
local obj = {}
obj.name = name
obj.commandTable = commandTable
obj.othersFunc = othersFunc
obj.commandWatcher = {}
obj.enable = function(self)
info(self.name.." start")
self.commandWatcher:start()
end
obj.disable = function(self)
info(self.name.." end")
self.commandWatcher:stop()
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
end
markMode = SubMode.new(
-- Ctrl + Space が押されたあとの処理
"Mark Mode",
{
{modifiers = {ctrl = true}, key = 'space'}, -- only disables mark mode
{modifiers = {ctrl = true}, key = 'g'}, -- only disables mark mode
{modifiers = {ctrl = true}, key = 'w', func = pressKeyFunc({'cmd'}, 'x')},
{modifiers = {alt = true}, key = 'w', func = pressKeyFunc({'cmd'}, 'c')},
{modifiers = {ctrl = true}, key = 'd', func = pressKeyFunc({}, 'forwarddelete')}
},
function(tapEvent) -- force shift on
flags = tapEvent:getFlags()
flags.shift = true
tapEvent:setFlags(flags)
return false
end
)
commandMode = SubMode.new(
-- Ctrl + X が押されたあとの処理
"Command Mode",
{
{modifiers = {ctrl = true}, key = 'g'}, -- only disables command mode
{modifiers = {ctrl = true}, key = 'f', func = pressKeyFunc({'cmd', 'shift'}, 'o')},
{modifiers = {ctrl = true}, key = 's', func = pressKeyFunc({'cmd'}, 's')},
-- next tab
{modifiers = {}, key = 'n', func = pressKeyFunc({'cmd', 'shift'}, JIS_RIGHT_BRACKET_CODE)},
-- previous tab
{modifiers = {}, key = 'p', func = pressKeyFunc({'cmd', 'shift'}, JIS_LEFT_BRACKET_CODE)},
-- close tab
{modifiers = {}, key = 'k', func = pressKeyFunc({'cmd'}, 'w')},
}
)
xcodeBindings = {
-- mark mode
hs.hotkey.new({'ctrl'}, 'space', function() markMode:enable() end),
-- command mode
hs.hotkey.new({'ctrl'}, 'x', function() commandMode:enable() end),
-- etc
-- jump to beginning/end of document
createKeyRemap({'alt', 'shift'}, ',', {'cmd'}, 'up'),
createKeyRemap({'alt', 'shift'}, '.', {'cmd'}, 'down'),
-- move to up/down of line. for popup window
createKeyRemap({'ctrl'}, 'p', {}, 'up'),
createKeyRemap({'ctrl'}, 'n', {}, 'down'),
-- undo
createKeyRemap({'ctrl'}, '/', {'cmd'}, 'z'),
-- redo
createKeyRemap({'ctrl'}, "'", {'cmd', 'shift'}, 'z'),
-- search
createKeyRemap({'ctrl'}, 's', {'cmd'}, 'f'),
-- paste
createKeyRemap({'ctrl'}, 'y', {'cmd'}, 'v'),
-- kill line
hs.hotkey.new({'ctrl'}, 'k', function()
markMode:enable()
pressKey({'ctrl'}, 'e')
pressKey({'ctrl'}, 'w')
end),
-- page up
createKeyRemap({'alt'}, 'v', {}, 'pageup')
}
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)
-- for debug
local function showKeyPress(tapEvent)
local code = tapEvent:getKeyCode()
local charactor = hs.keycodes.map[tapEvent:getKeyCode()]
hs.alert.show(tostring(code)..":"..charactor, 1.5)
end
local keyTap = hs.eventtap.new( {hs.eventtap.event.types.keyDown}, showKeyPress)
k = hs.hotkey.modal.new({"cmd", "shift", "ctrl"}, 'P')
function k:entered()
hs.alert.show("Enabling Keypress Show Mode", 1.5)
keyTap:start()
end
function k:exited()
hs.alert.show("Disabling Keypress Show Mode", 1.5)
end
k:bind({"cmd", "shift", "ctrl"}, 'P', function()
keyTap:stop()
k:exit()
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment