Skip to content

Instantly share code, notes, and snippets.

@manugill
Created May 4, 2017 02:37
Show Gist options
  • Select an option

  • Save manugill/754422145794a75c29dabf3e37ba98d3 to your computer and use it in GitHub Desktop.

Select an option

Save manugill/754422145794a75c29dabf3e37ba98d3 to your computer and use it in GitHub Desktop.
.hammerspoon
-----------------------------------------------
-- Setup:
-- Using Seil: Remap capslock to F19
-- Using Karabiner: Remap F19 to hyper (cmd+alt+ctrl+shift)
-----------------------------------------------
-----------------------------------------------
-- Base variables and settings
-----------------------------------------------
-- keyboard modifiers for bindings
local mod = {
cc = { 'cmd', 'ctrl' },
ca = { 'cmd', 'alt' },
cac = { 'cmd', 'alt', 'ctrl' },
cas = { 'cmd', 'alt', 'shift' },
cacs = { 'cmd', 'alt', 'ctrl', 'shift' }, -- hyper
}
-- extensions, available in hammerspoon console
ext = {
frame = {},
win = {},
app = {},
utils = {},
cache = {},
watchers = {}
}
-- extension settings
ext.win.animationDuration = 0.15
ext.win.margin = 6
ext.win.fixEnabled = false
ext.win.fullFrame = true
-- hs settings
hs.window.animationDuration = ext.win.animationDuration
-----------------------------------------------
-- Launch or focus on applications
-----------------------------------------------
-- force focus
function ext.app.forceLaunchOrFocus(appName)
-- first focus with hammerspoon
hs.application.launchOrFocus(appName)
-- clear timer if exists
if ext.cache.launchTimer then ext.cache.launchTimer:stop() end
-- wait 500ms for window to appear and try hard to show the window
ext.cache.launchTimer = hs.timer.doAfter(0.5, function()
local frontmostApp = hs.application.frontmostApplication()
local frontmostWindows = hs.fnutils.filter(frontmostApp:allWindows(), function(win) return win:isStandard() end)
-- break if this app is not frontmost (when/why?)
if frontmostApp:title() ~= appName then
print('Expected app in front: ' .. appName .. ' got: ' .. frontmostApp:title())
return
end
if #frontmostWindows == 0 then
-- check if there's app name in window menu (Calendar, Messages, etc...)
if frontmostApp:findMenuItem({ 'Window', appName }) then
-- select it, usually moves to space with this window
frontmostApp:selectMenuItem({ 'Window', appName })
else
-- otherwise send cmd-n to create new window
hs.eventtap.keyStroke({ 'cmd' }, 'n')
end
end
end)
end
-- smart app launch or focus or cycle windows
function ext.app.smartLaunchOrFocus(launchApps)
local frontmostWindow = hs.window.frontmostWindow()
local runningApps = hs.application.runningApplications()
local runningWindows = {}
-- filter running applications by apps array
local runningApps = hs.fnutils.map(launchApps, function(launchApp)
return hs.application.get(launchApp)
end)
-- create table of sorted windows per application
hs.fnutils.each(runningApps, function(runningApp)
local standardWindows = hs.fnutils.filter(runningApp:allWindows(), function(win)
return win:isStandard()
end)
table.sort(standardWindows, function(a, b) return a:id() < b:id() end)
runningWindows = standardWindows
end)
if #runningApps == 0 then
-- if no apps are running then launch first one in list
ext.app.forceLaunchOrFocus(launchApps[1])
elseif #runningWindows == 0 then
-- if some apps are running, but no windows - force create one
ext.app.forceLaunchOrFocus(runningApps[1]:title())
else
-- check if one of windows is already focused
local currentIndex = hs.fnutils.indexOf(runningWindows, frontmostWindow)
if not currentIndex then
-- if none of them is selected focus the first one
runningWindows[1]:focus()
else
-- otherwise cycle through all the windows
local newIndex = currentIndex + 1
if newIndex > #runningWindows then newIndex = 1 end
runningWindows[newIndex]:focus()
end
end
end
-- bind shortcuts
hs.fnutils.each({
{ key = '`', apps = { 'Hyper' } },
-- { key = '`', apps = { 'iTerm' } },
{ key = 'r', apps = { 'Google Chrome' } }, -- b(r)owser, review, ch(r)ome
{ key = 't', apps = { 'FirefoxDeveloperEdition' } }, -- web
{ key = 'e', apps = { 'Visual Studio Code' } }, -- editor
{ key = 'f', apps = { 'Finder' } }, -- find
{ key = 'c', apps = { 'Slack' } }, -- chat
{ key = 'o', apps = { 'Microsoft Outlook' } }, -- outlook
{ key = 'd', apps = { 'Sketch' } }, -- design
-- { key = 'd', apps = { 'Preview' } }, -- design
--{ key = 'd', apps = { 'Adobe Photoshop CC 2015.app', } }, -- design
--{ key = 'd', apps = { 'Adobe InDesign CC 2015.app', } }, -- design
--{ key = 'd', apps = { 'Microsoft Word' } }, -- document
-- { key = 'g', apps = { 'GitKraken' } }, -- git
{ key = 'g', apps = { 'SmartGit' } }, -- git
{ key = 'v', apps = { 'VirtualBoxVM' } }, -- vm
{ key = 'u', apps = { 'Transmit' } }, -- upload
--{ key = '0', apps = { 'MAMP PRO' } }, -- undefined
{ key = 'h', apps = { 'Helium' } }, -- helium
{ key = 'm', apps = { 'Spotify' } }, -- music
{ key = 'p', apps = { 'Digital Color Meter' } }, -- picker
}, function(object)
hs.hotkey.bind(mod.cacs, object.key, function() ext.app.smartLaunchOrFocus(object.apps) end)
end)
-- window hints on tab
hs.hotkey.bind(mod.cacs, 'tab', function()
hs.hints.windowHints()
end)
-----------------------------------------------
-- Window management: fullscreen, half size, move across screens (west and east only)
-----------------------------------------------
hs.hotkey.bind(mod.cacs, 'LEFT', function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
if f.x == max.x and f.w == max.w / 2 then
win:moveOneScreenNorth()
else
f.x = max.x
f.y = max.y
f.w = max.w / 2
f.h = max.h
win:setSize(f, 0)
win:setFrame(f, 0)
end
end)
hs.hotkey.bind(mod.cacs, 'RIGHT', function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
if f.x == max.x + (max.w / 2) and f.w == max.w / 2 then
win:moveOneScreenSouth()
else
f.x = max.x + (max.w / 2)
f.y = max.y
f.w = max.w / 2
f.h = max.h
win:setSize(f, 0)
win:setFrame(f, 0)
end
end)
hs.hotkey.bind(mod.cacs, 'UP', function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
f.x = max.x
f.y = max.y
f.w = max.w
f.h = max.h
win:maximize(0)
win:setFrame(f, 0)
end)
hs.hotkey.bind(mod.cacs, 'DOWN', function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
f.w = max.w * 0.8
f.h = max.h * 0.8
local winSize = win:size()
win:setSize(f, 0)
win:centerOnScreen()
end)
hs.hotkey.bind(mod.cacs, '-', function()
local windows = hs.window.orderedWindows()
hs.fnutils.ieach(windows, function (win)
win:minimize(0.1)
end)
end)
hs.hotkey.bind(mod.cacs, '=', function()
local windows = hs.window.orderedWindows()
hs.fnutils.ieach(windows, function (win)
win:maximize(0.1)
end)
end)
-----------------------------------------------
-- Reload config on write
-----------------------------------------------
function reload_config(files)
hs.reload()
end
hs.pathwatcher.new(os.getenv('HOME') .. '/.hammerspoon/', reload_config):start()
hs.alert.show('Config loaded')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment