Initial commit of Hammerspoon config
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
--- === Seal.plugins.apps ===
|
||||
---
|
||||
--- A plugin to add launchable apps/scripts, making Seal act as a launch bar
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
obj.__name = "seal_apps"
|
||||
obj.appCache = {}
|
||||
|
||||
--- Seal.plugins.apps.appSearchPaths
|
||||
--- Variable
|
||||
--- Table containing the paths to search for launchable items
|
||||
---
|
||||
--- Notes:
|
||||
--- * If you change this, you will need to call `spoon.Seal.plugins.apps:restart()` to force Spotlight to search for new items.
|
||||
obj.appSearchPaths = {
|
||||
"/Applications",
|
||||
"/System/Applications",
|
||||
"~/Applications",
|
||||
"/Developer/Applications",
|
||||
"/Applications/Xcode.app/Contents/Applications",
|
||||
"/System/Library/PreferencePanes",
|
||||
"/Library/PreferencePanes",
|
||||
"~/Library/PreferencePanes",
|
||||
"/System/Library/CoreServices/Applications",
|
||||
"/System/Library/CoreServices/",
|
||||
"/usr/local/Cellar",
|
||||
"/Library/Scripts",
|
||||
"~/Library/Scripts"
|
||||
}
|
||||
|
||||
local modifyNameMap = function(info, add)
|
||||
for _, item in ipairs(info) do
|
||||
icon = nil
|
||||
local displayname = item.kMDItemDisplayName or hs.fs.displayName(item.kMDItemPath)
|
||||
displayname = displayname:gsub("%.app$", "", 1)
|
||||
if string.find(item.kMDItemPath, "%.prefPane$") then
|
||||
displayname = displayname .. " preferences"
|
||||
if add then
|
||||
icon = hs.image.iconForFile(item.kMDItemPath)
|
||||
end
|
||||
end
|
||||
if add then
|
||||
bundleID = item.kMDItemCFBundleIdentifier
|
||||
if (not icon) and (bundleID) then
|
||||
icon = hs.image.imageFromAppBundle(bundleID)
|
||||
end
|
||||
obj.appCache[displayname] = {
|
||||
path = item.kMDItemPath,
|
||||
bundleID = bundleID,
|
||||
icon = icon
|
||||
}
|
||||
else
|
||||
obj.appCache[displayname] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local updateNameMap = function(obj, msg, info)
|
||||
if info then
|
||||
-- all three can occur in either message, so check them all!
|
||||
if info.kMDQueryUpdateAddedItems then modifyNameMap(info.kMDQueryUpdateAddedItems, true) end
|
||||
if info.kMDQueryUpdateChangedItems then modifyNameMap(info.kMDQueryUpdateChangedItems, true) end
|
||||
if info.kMDQueryUpdateRemovedItems then modifyNameMap(info.kMDQueryUpdateRemovedItems, false) end
|
||||
else
|
||||
-- shouldn't happen for didUpdate or inProgress
|
||||
print("~~~ userInfo from SpotLight was empty for " .. msg)
|
||||
end
|
||||
end
|
||||
|
||||
--- Seal.plugins.apps:start()
|
||||
--- Method
|
||||
--- Starts the Spotlight app searcher
|
||||
---
|
||||
--- Parameters:
|
||||
--- * None
|
||||
---
|
||||
--- Returns:
|
||||
--- * None
|
||||
---
|
||||
--- Notes:
|
||||
--- * This is called automatically when the plugin is loaded
|
||||
function obj:start()
|
||||
obj.spotlight = hs.spotlight.new():queryString([[ (kMDItemContentType = "com.apple.application-bundle") || (kMDItemContentType = "com.apple.systempreference.prefpane") || (kMDItemContentType = "com.apple.applescript.text") || (kMDItemContentType = "com.apple.applescript.script") ]])
|
||||
:callbackMessages("didUpdate", "inProgress")
|
||||
:setCallback(updateNameMap)
|
||||
:searchScopes(obj.appSearchPaths)
|
||||
:start()
|
||||
end
|
||||
|
||||
--- Seal.plugins.apps:stop()
|
||||
--- Method
|
||||
--- Stops the Spotlight app searcher
|
||||
---
|
||||
--- Parameters:
|
||||
--- * None
|
||||
---
|
||||
--- Returns:
|
||||
--- * None
|
||||
function obj:stop()
|
||||
obj.spotlight:stop()
|
||||
obj.spotlight = nil
|
||||
obj.appCache = {}
|
||||
end
|
||||
|
||||
--- Seal.plugins.apps:restart()
|
||||
--- Method
|
||||
--- Restarts the Spotlight app searcher
|
||||
---
|
||||
--- Parameters:
|
||||
--- * None
|
||||
---
|
||||
--- Returns:
|
||||
--- * None
|
||||
function obj:restart()
|
||||
self:stop()
|
||||
self:start()
|
||||
end
|
||||
|
||||
hs.application.enableSpotlightForNameSearches(true)
|
||||
obj:start()
|
||||
|
||||
function obj:commands()
|
||||
return {kill = {
|
||||
cmd = "kill",
|
||||
fn = obj.choicesKillCommand,
|
||||
plugin = obj.__name,
|
||||
name = "Kill",
|
||||
description = "Kill an application"
|
||||
},
|
||||
reveal = {
|
||||
cmd = "reveal",
|
||||
fn = obj.choicesRevealCommand,
|
||||
plugin = obj.__name,
|
||||
name = "Reveal",
|
||||
description = "Reveal an application in the Finder"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
function obj:bare()
|
||||
return self.choicesApps
|
||||
end
|
||||
|
||||
function obj.choicesApps(query)
|
||||
local choices = {}
|
||||
if query == nil or query == "" then
|
||||
return choices
|
||||
end
|
||||
for name,app in pairs(obj.appCache) do
|
||||
if string.match(name:lower(), query:lower()) then
|
||||
local choice = {}
|
||||
local instances = {}
|
||||
if app["bundleID"] then
|
||||
instances = hs.application.applicationsForBundleID(app["bundleID"])
|
||||
end
|
||||
if #instances > 0 then
|
||||
choice["text"] = name .. " (Running)"
|
||||
else
|
||||
choice["text"] = name
|
||||
end
|
||||
choice["subText"] = app["path"]
|
||||
if app["icon"] then
|
||||
choice["image"] = app["icon"]
|
||||
end
|
||||
choice["path"] = app["path"]
|
||||
choice["uuid"] = obj.__name.."__"..(app["bundleID"] or name)
|
||||
choice["plugin"] = obj.__name
|
||||
choice["type"] = "launchOrFocus"
|
||||
table.insert(choices, choice)
|
||||
end
|
||||
end
|
||||
return choices
|
||||
end
|
||||
|
||||
function obj.choicesKillCommand(query)
|
||||
local choices = {}
|
||||
if query == nil then
|
||||
return choices
|
||||
end
|
||||
local apps = hs.application.runningApplications()
|
||||
for k, app in pairs(apps) do
|
||||
local name = app:name()
|
||||
if string.match(name:lower(), query:lower()) and app:mainWindow() then
|
||||
local choice = {}
|
||||
choice["text"] = "Kill "..name
|
||||
choice["subText"] = app:path().." PID: "..app:pid()
|
||||
choice["pid"] = app:pid()
|
||||
choice["plugin"] = obj.__name
|
||||
choice["type"] = "kill"
|
||||
choice["image"] = hs.image.imageFromAppBundle(app:bundleID())
|
||||
table.insert(choices, choice)
|
||||
end
|
||||
end
|
||||
return choices
|
||||
end
|
||||
|
||||
function obj.choicesRevealCommand(query)
|
||||
local choices = {}
|
||||
if query == nil then
|
||||
return choices
|
||||
end
|
||||
local apps = obj.choicesApps(query)
|
||||
for k, app in pairs(apps) do
|
||||
local name = app.text
|
||||
if string.match(name:lower(), query:lower()) then
|
||||
local choice = {}
|
||||
choice["text"] = "Reveal "..name
|
||||
choice["path"] = app.path
|
||||
choice["subText"] = app.path
|
||||
choice["plugin"] = obj.__name
|
||||
choice["type"] = "reveal"
|
||||
if app.image then
|
||||
choice["image"] = app.image
|
||||
end
|
||||
table.insert(choices, choice)
|
||||
end
|
||||
end
|
||||
return choices
|
||||
end
|
||||
|
||||
function obj.completionCallback(rowInfo)
|
||||
if rowInfo["type"] == "launchOrFocus" then
|
||||
if string.find(rowInfo["path"], "%.applescript$") or string.find(rowInfo["path"], "%.scpt$") then
|
||||
hs.task.new("/usr/bin/osascript", nil, { rowInfo["path"] }):start()
|
||||
else
|
||||
hs.task.new("/usr/bin/open", nil, { rowInfo["path"] }):start()
|
||||
end
|
||||
elseif rowInfo["type"] == "kill" then
|
||||
hs.application.get(rowInfo["pid"]):kill()
|
||||
elseif rowInfo["type"] == "reveal" then
|
||||
hs.osascript.applescript(string.format([[tell application "Finder" to reveal (POSIX file "%s")]], rowInfo["path"]))
|
||||
hs.application.launchOrFocus("Finder")
|
||||
end
|
||||
end
|
||||
|
||||
return obj
|
||||
Reference in New Issue
Block a user