Skip to content

Instantly share code, notes, and snippets.

@juanmiret
Last active July 23, 2018 18:18
Show Gist options
  • Save juanmiret/81a21a5ae8d6b6061b0a4e72a8be61b2 to your computer and use it in GitHub Desktop.
Save juanmiret/81a21a5ae8d6b6061b0a4e72a8be61b2 to your computer and use it in GitHub Desktop.
Simple Vi mode with Karabiner-elements and Hammerspoon
local module = {}
module.debugging = false -- whether to print status updates
local eventtap = require "hs.eventtap"
local event = eventtap.event
local inspect = require "hs.inspect"
local keyHandler = function(e)
local watchFor = {
h = "left",
j = "down",
k = "up",
l = "right",
u = "delete",
i = "forwarddelete"
}
local actualKey = e:getCharacters(true)
local replacement = watchFor[actualKey:lower()]
if replacement then
local isDown = e:getType() == event.types.keyDown
local flags = {}
for k, v in pairs(e:getFlags()) do
if v and k ~= "fn" then -- fn will be down because that's our "wrapper", so ignore it
table.insert(flags, k)
end
end
if module.debugging then print("viKeys: " .. replacement, inspect(flags), isDown) end
local replacementEvent = event.newKeyEvent(flags, replacement, isDown)
if isDown then
-- allow for auto-repeat
replacementEvent:setProperty(event.properties.keyboardEventAutorepeat, e:getProperty(event.properties.keyboardEventAutorepeat))
end
return true, { replacementEvent }
else
return false -- do nothing to the event, just pass it along
end
end
local modifierHandler = function(e)
local flags = e:getFlags()
local onlyControlPressed = false
for k, v in pairs(flags) do
onlyControlPressed = v and k == "fn"
if not onlyControlPressed then break end
end
-- you must tap and hold fn by itself to turn this on
if onlyControlPressed and not module.keyListener then
if module.debugging then print("viKeys: keyhandler on") end
module.keyListener = eventtap.new({ event.types.keyDown, event.types.keyUp }, keyHandler):start()
-- however, adding additional modifiers afterwards is ok... its only when fn isn't down that we switch back off
elseif not flags.fn and module.keyListener then
if module.debugging then print("viKeys: keyhandler off") end
module.keyListener:stop()
module.keyListener = nil
end
return false
end
module.modifierListener = eventtap.new({ event.types.flagsChanged }, modifierHandler)
module.start = function()
module.modifierListener:start()
end
module.stop = function()
if module.keyListener then
module.keyListener:stop()
module.keyListener = nil
end
module.modifierListener:stop()
end
module.start() -- autostart
return module
{
"profiles": [{
"devices": [],
"name": "Default profile",
"selected": true,
"simple_modifications": {
"caps_lock": "fn"
},
"standalone_keys": {
"caps_lock": "caps_lock"
},
"one_to_many_mappings": {},
"virtual_hid_keyboard": {
"caps_lock_delay_milliseconds": 0,
"keyboard_type": "ansi",
"standalone_keys_delay_milliseconds": 100
}
}]
}
@maicher
Copy link

maicher commented Apr 8, 2017

It works. Thanks a lot!

@jesseleite
Copy link

Love it! Can't wait until vi mode comes back into karabiner-elements, but until then, thank you 👌

@jpablobr
Copy link

Thanks!

@juanmiret
Copy link
Author

@sengngykouch hey i'm sorry i didn't answer on time, I never saw the notification. I think the solution is what alf says.

You have to use the version of Karabiner-Elements by @wwwjfy for this to work. See https://github.com/wwwjfy/Karabiner-Elements/releases for releases and pqrs-org/Karabiner-Elements#247 for the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment