From 6faa77c9ed945d877d087c01c9ec0ab9a4cd032a Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Fri, 25 Dec 2015 06:43:34 -0500 Subject: [PATCH] Rewrite per-app framework --- hammerspoon/apps.lua | 19 ----- hammerspoon/apps/finder.lua | 5 ++ hammerspoon/apps/iterm.lua | 22 +++++ hammerspoon/core.lua | 56 ------------- hammerspoon/init.lua | 21 ++--- hammerspoon/util.lua | 161 ++++++++++++++++++++++++++++++++++++ 6 files changed, 199 insertions(+), 85 deletions(-) delete mode 100644 hammerspoon/apps.lua create mode 100644 hammerspoon/apps/finder.lua create mode 100644 hammerspoon/apps/iterm.lua delete mode 100644 hammerspoon/core.lua create mode 100644 hammerspoon/util.lua diff --git a/hammerspoon/apps.lua b/hammerspoon/apps.lua deleted file mode 100644 index c5579f1f..00000000 --- a/hammerspoon/apps.lua +++ /dev/null @@ -1,19 +0,0 @@ - -forApp("Finder", function (appName, eventType, appObject) - -- Bring all Finder windows forward when one gets activated - appObject:selectMenuItem({"Window", "Bring All to Front"}) -end) - -forApp("iTerm2", function (appName, _, appObject) - -- cmd+N switch-to-window shortcuts in Tmux - for i = 1,9 do - i = tostring(i) - bind(appName, 'cmd', i, function () - if string.find(appObject:focusedWindow():title(), ".*tmux") then - -- os.execute("/usr/local/bin/tmux select-window -t " .. i) - hs.eventtap.keyStroke({'ctrl'}, 'c') - hs.eventtap.keyStroke({}, i) - end - end) - end -end) diff --git a/hammerspoon/apps/finder.lua b/hammerspoon/apps/finder.lua new file mode 100644 index 00000000..70c8e885 --- /dev/null +++ b/hammerspoon/apps/finder.lua @@ -0,0 +1,5 @@ + +App.new("Finder") + .onActivate(function(self, app) + app:selectMenuItem({"Window", "Bring All to Front"}) + end) diff --git a/hammerspoon/apps/iterm.lua b/hammerspoon/apps/iterm.lua new file mode 100644 index 00000000..f75b9487 --- /dev/null +++ b/hammerspoon/apps/iterm.lua @@ -0,0 +1,22 @@ + +local tmux_prefix = {{'ctrl'}, 'c'} + +local function isTmuxWindow() + return function () + hs.window.focusedWindow():title():find("tmux") + end +end + +App.new("iTerm2") + .onInit(function (self) + -- cmd+N for switching tmux windows + for i = 1,9 do + i = tostring(i) + self.bind({'cmd'}, i, function() + os.execute("/usr/local/bin/tmux select-window -t " .. i) + end, isTmuxWindow) + end + end) + .bind({'cmd'}, 'n', invoke({tmux_prefix, 'c'}), isTmuxWindow) -- new window + .bind({'cmd'}, 'w', invoke({tmux_prefix, 'x'}), isTmuxWindow) -- close pane/window + .bind({'cmd', 'shift'}, 'w', util.call(hs.window, 'close')) -- close pane/window diff --git a/hammerspoon/core.lua b/hammerspoon/core.lua deleted file mode 100644 index cc4aa265..00000000 --- a/hammerspoon/core.lua +++ /dev/null @@ -1,56 +0,0 @@ --- TODO: Turn into App class - -core = {} -core.__index = core - -function core.reload() - hs.reload() - hs.alert("Config reloaded!") -end - --- App-specific code, runs when you launch or switch to another app -apps = {} -function forApp(name, func) - if type(name) == 'table' then - for i = 1, #name do - apps[name[i]] = func - end - else - apps[name] = func - end -end - --- App-specific keybinds -lastApp = "" -binds = {} -function bind(app, mods, key, pressedfn, releasedfn, repeatfn) - if not binds[app] then - binds[app] = {} - end - if not binds[app][mods] then - binds[app][mods] = {} - end - if binds[app][mods][key] then - binds[app][mods][key]:enable() - else - binds[app][mods][key] = hs.hotkey.bind(mods, key, nil, pressedfn, releasedfn, repeatfn) - end -end - -function appWatcher(appName, eventType, appObject) - if (eventType == hs.application.watcher.activated) then - if (lastApp ~= appName) and (binds[lastApp]) then - for _, i in pairs(binds[lastApp]) do - for _, j in pairs(i) do - j:disable() - end - end - end - if apps[appName] then - apps[appName](appName, eventType, appObject) - lastApp = appName - end - end -end -local appWatcher = hs.application.watcher.new(appWatcher) -appWatcher:start() diff --git a/hammerspoon/init.lua b/hammerspoon/init.lua index 315888a1..88d58f59 100644 --- a/hammerspoon/init.lua +++ b/hammerspoon/init.lua @@ -1,14 +1,15 @@ -require('core') +-- Core +util = require('util') --- -require('modules/windows') - --- Globals -screen = hs.screen.primaryScreen() +-- Settings +hs.window.animationDuration = 0 -- Modules -require('keybinds') -require('apps') -require('urls') -require('locations') +-- require('apps') +-- require('urls') +-- require('locations') +util.autoimport("./apps") + +-- +require('bindings') diff --git a/hammerspoon/util.lua b/hammerspoon/util.lua new file mode 100644 index 00000000..d440dbc6 --- /dev/null +++ b/hammerspoon/util.lua @@ -0,0 +1,161 @@ +-- TODO: Turn into App class + +local module = { + apps = {}, + bindings = {}, + + ls = function(directory) + local i, t, popen = 0, {}, io.popen + for filename in popen('ls -a "'..directory..'"'):lines() do + i = i + 1 + t[i] = filename + end + return t + end, + + reload = function() + hs.reload() + hs.alert("Config reloaded!") + end, + + call = function(obj, fn) + return function () return obj[fn](obj) end + end, + + autoimport = function(dir) + for _, file in pairs(util.ls(dir)) do + if file:find(".+%.lua") then + local a = string.gsub(file, '%.lua', '') + print(a .. " loaded") + require(dir .. "/" .. a) + end + end + end +} + +-- App-specific code, runs when you launch or switch to another app +local apps = {} +App = {} +App.new = function(name) + local self = {} + self.name = name + self.bindings = {} + + self.onInitFn = nil + self.onActivateFn = nil + self.onDeactivateFn = nil + self.onLaunchedFn = nil + self.onTerminatedFn = nil + + self.bind = function(mods, key, action, predicate) + table.insert(self.bindings, + {key = hs.hotkey.new(mods, key, action), predicate = predicate}) + return self + end + + self.onInit = function(fn) + fn(self) + return self + end + self.onActivate = function(fn) + self.onActivateFn = fn + return self + end + self.onDeactivate = function(fn) + self.onDeactivateFn = fn + return self + end + self.onLaunched = function(fn) + self.onLaunchedFn = fn + return self + end + self.onTerminated = function(fn) + self.onTerminatedFn = fn + return self + end + + self.activate = function(appObject) + if type(self.onActivateFn) == 'function' then + self:onActivateFn(appObject) + end + self:enableBinds() + end + self.deactivate = function(appObject) + if type(self.onDeactivateFn) == 'function' then + self:onDeactivateFn(appObject) + end + self:disableBinds() + end + self.launch = function(appObject) + if type(self.onLaunchedFn) == 'function' then + self:onLaunchedFn(appObject) + end + end + self.terminate = function(appObject) + if type(self.onTerminatedFn) == 'function' then + self:onTerminatedFn(appObject) + end + self:clearBinds() + end + + self.enableBinds = function(self) + hs.fnutils.each(self.bindings, function(b) + if b.predicate and not b.predicate() then + b.key:disable() + else + b.key:enable() + end + end) + end + + self.disableBinds = function(self) + hs.fnutils.each(self.bindings, function(b) b.key:disable() end) + end + + self.clearBinds = function(self) + hs.fnutils.each(self.bindings, function(b) b.key:delete() end) + self.bindings = {} + end + + apps[name] = self + return self +end + +function invoke(keys, predicate) + return function () + if predicate and not predicate() then + return + end + if type(keys) == 'table' then + hs.fnutils.each(keys, function (key) + if type(key) ~= 'table' then + key = {{}, key} + end + hs.eventtap.keyStroke(key[1], key[2]) + end) + elseif type(keys) == 'string' then + os.execute(keys) + elseif type(keys) == 'function' then + return keys() + end + end +end + +-- +function appGlobalWatcher(appName, eventType, appObject) + if apps[appName] then + if (eventType == hs.application.watcher.activated) then + apps[appName].activate(appObject) + elseif (eventType == hs.application.watcher.deactivated) then + apps[appName].deactivate(appObject) + elseif (eventType == hs.application.watcher.launched) then + apps[appName].launch(appObject) + elseif (eventType == hs.application.watcher.terminated) then + apps[appName].terminate(appObject) + end + end +end +hs.application.watcher.new(appGlobalWatcher):start() + +-- +return module