mirror of https://github.com/einverne/dotfiles.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
8.8 KiB
257 lines
8.8 KiB
4 years ago
|
--- === HSearch ===
|
||
|
---
|
||
|
--- Hammerspoon Search
|
||
|
---
|
||
|
--- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/HSearch.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/HSearch.spoon.zip)
|
||
|
|
||
|
local obj={}
|
||
|
obj.__index = obj
|
||
|
|
||
|
-- Metadata
|
||
|
obj.name = "HSearch"
|
||
|
obj.version = "1.0"
|
||
|
obj.author = "ashfinal <[email protected]>"
|
||
|
obj.homepage = "https://github.com/Hammerspoon/Spoons"
|
||
|
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||
|
|
||
|
-- Internal function used to find our location, so we know where to load files from
|
||
|
local function script_path()
|
||
|
local str = debug.getinfo(2, "S").source:sub(2)
|
||
|
return str:match("(.*/)")
|
||
|
end
|
||
|
|
||
|
obj.spoonPath = script_path()
|
||
|
|
||
|
obj.sources = {}
|
||
|
obj.sources_overview = {}
|
||
|
obj.search_path = {hs.configdir .. "/private/hsearch_dir", obj.spoonPath}
|
||
|
obj.hotkeys = {}
|
||
|
obj.source_kw = nil
|
||
|
|
||
|
function obj:restoreOutput()
|
||
|
obj.output_pool = {}
|
||
|
-- Define the built-in output type
|
||
|
local function openWithBrowser(arg)
|
||
|
local default_browser = hs.urlevent.getDefaultHandler('http')
|
||
|
hs.urlevent.openURLWithBundle(arg, default_browser)
|
||
|
end
|
||
|
local function openWithSafari(arg)
|
||
|
hs.urlevent.openURLWithBundle(arg, "com.apple.Safari")
|
||
|
end
|
||
|
local function openWithChrome(arg)
|
||
|
hs.urlevent.openURLWithBundle(arg, "com.google.Chrome")
|
||
|
end
|
||
|
local function openWithFirefox(arg)
|
||
|
hs.urlevent.openURLWithBundle(arg, "org.mozilla.firefox")
|
||
|
end
|
||
|
local function copyToClipboard(arg)
|
||
|
hs.pasteboard.setContents(arg)
|
||
|
end
|
||
|
local function sendKeyStrokes(arg)
|
||
|
local cwin = hs.window.orderedWindows()[1]
|
||
|
cwin:focus()
|
||
|
hs.eventtap.keyStrokes(arg)
|
||
|
end
|
||
|
obj.output_pool["browser"] = openWithBrowser
|
||
|
obj.output_pool["safari"] = openWithSafari
|
||
|
obj.output_pool["chrome"] = openWithChrome
|
||
|
obj.output_pool["firefox"] = openWithFirefox
|
||
|
obj.output_pool["clipboard"] = copyToClipboard
|
||
|
obj.output_pool["keystrokes"] = sendKeyStrokes
|
||
|
end
|
||
|
|
||
|
function obj:init()
|
||
|
obj.chooser = hs.chooser.new(function(chosen)
|
||
|
obj.trigger:disable()
|
||
|
-- Disable all hotkeys
|
||
|
for _,val in pairs(obj.hotkeys) do
|
||
|
for i=1,#val do
|
||
|
val[i]:disable()
|
||
|
end
|
||
|
end
|
||
|
if chosen ~= nil then
|
||
|
if chosen.output then
|
||
|
obj.output_pool[chosen.output](chosen.arg)
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
obj.chooser:rows(9)
|
||
|
end
|
||
|
|
||
|
--- HSearch:switchSource()
|
||
|
--- Method
|
||
|
--- Tigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual.
|
||
|
---
|
||
|
|
||
|
function obj:switchSource()
|
||
|
local querystr = obj.chooser:query()
|
||
|
if string.len(querystr) > 0 then
|
||
|
local matchstr = string.match(querystr, "^%w+")
|
||
|
if matchstr == querystr then
|
||
|
-- First we try to switch source according to the querystr
|
||
|
if obj.sources[querystr] then
|
||
|
obj.source_kw = querystr
|
||
|
obj.chooser:query('')
|
||
|
obj.chooser:choices(nil)
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
obj.sources[querystr]()
|
||
|
else
|
||
|
local row_content = obj.chooser:selectedRowContents()
|
||
|
local row_kw = row_content.keyword
|
||
|
-- Then try to switch source according to selected row
|
||
|
if obj.sources[row_kw] then
|
||
|
obj.source_kw = row_kw
|
||
|
obj.chooser:query('')
|
||
|
obj.chooser:choices(nil)
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
obj.sources[row_kw]()
|
||
|
else
|
||
|
obj.source_kw = nil
|
||
|
local chooser_data = {
|
||
|
{text="No source found!", subText="Maybe misspelled the keyword?"},
|
||
|
{text="Want to add your own source?", subText="Feel free to read the code and open PRs. :)"}
|
||
|
}
|
||
|
obj.chooser:choices(chooser_data)
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
hs.eventtap.keyStroke({"cmd"}, "a")
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
obj.source_kw = nil
|
||
|
local chooser_data = {
|
||
|
{text="Invalid Keyword", subText="Trigger keyword must only consist of alphanumeric characters."}
|
||
|
}
|
||
|
obj.chooser:choices(chooser_data)
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
hs.eventtap.keyStroke({"cmd"}, "a")
|
||
|
end
|
||
|
else
|
||
|
local row_content = obj.chooser:selectedRowContents()
|
||
|
local row_kw = row_content.keyword
|
||
|
if obj.sources[row_kw] then
|
||
|
obj.source_kw = row_kw
|
||
|
obj.chooser:query('')
|
||
|
obj.chooser:choices(nil)
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
obj.sources[row_kw]()
|
||
|
else
|
||
|
obj.source_kw = nil
|
||
|
-- If no matching source then show sources overview
|
||
|
local chooser_data = obj.sources_overview
|
||
|
obj.chooser:choices(chooser_data)
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
end
|
||
|
end
|
||
|
if obj.source_kw then
|
||
|
for key,val in pairs(obj.hotkeys) do
|
||
|
if key == obj.source_kw then
|
||
|
for i=1,#val do
|
||
|
val[i]:enable()
|
||
|
end
|
||
|
else
|
||
|
for i=1,#val do
|
||
|
val[i]:disable()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
for _,val in pairs(obj.hotkeys) do
|
||
|
for i=1,#val do
|
||
|
val[i]:disable()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- HSearch:loadSources()
|
||
|
--- Method
|
||
|
--- Load new sources from `HSearch.search_path`, the search_path defaults to `~/.hammerspoon/private/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual.
|
||
|
---
|
||
|
|
||
|
function obj:loadSources()
|
||
|
obj.sources = {}
|
||
|
obj.sources_overview = {}
|
||
|
obj:restoreOutput()
|
||
|
for _,dir in ipairs(obj.search_path) do
|
||
|
local file_list = io.popen("find " .. dir .. " -type f -name '*.lua'")
|
||
|
for file in file_list:lines() do
|
||
|
-- Exclude self
|
||
|
if file ~= obj.spoonPath .. "/init.lua" then
|
||
|
local f = loadfile(file)
|
||
|
if f then
|
||
|
local source = f()
|
||
|
local output = source.new_output
|
||
|
if output then obj.output_pool[output.name] = output.func end
|
||
|
local overview = source.overview
|
||
|
-- Gather souces overview from files
|
||
|
table.insert(obj.sources_overview, overview)
|
||
|
local hotkey = source.hotkeys
|
||
|
if hotkey then obj.hotkeys[overview.keyword] = hotkey end
|
||
|
local function sourceFunc()
|
||
|
local notice = source.notice
|
||
|
if notice then obj.chooser:choices({notice}) end
|
||
|
local request = source.init_func
|
||
|
if request then
|
||
|
local chooser_data = request()
|
||
|
if chooser_data then
|
||
|
local desc = source.description
|
||
|
if desc then table.insert(chooser_data, 1, desc) end
|
||
|
end
|
||
|
obj.chooser:choices(chooser_data)
|
||
|
else
|
||
|
obj.chooser:choices(nil)
|
||
|
end
|
||
|
if source.callback then
|
||
|
obj.chooser:queryChangedCallback(source.callback)
|
||
|
else
|
||
|
obj.chooser:queryChangedCallback()
|
||
|
end
|
||
|
obj.chooser:searchSubText(true)
|
||
|
end
|
||
|
-- Add this source to sources pool, so it can found and triggered.
|
||
|
obj.sources[overview.keyword] = sourceFunc
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--- HSearch:toggleShow()
|
||
|
--- Method
|
||
|
--- Toggle the display of HSearch
|
||
|
---
|
||
|
|
||
|
function obj:toggleShow()
|
||
|
if #obj.sources_overview == 0 then
|
||
|
-- If it's the first time HSearch shows itself, then load all sources from files
|
||
|
obj:loadSources()
|
||
|
-- Show sources overview, so users know what to do next.
|
||
|
obj.chooser:choices(obj.sources_overview)
|
||
|
end
|
||
|
if obj.chooser:isVisible() then
|
||
|
obj.chooser:hide()
|
||
|
obj.trigger:disable()
|
||
|
for _,val in pairs(obj.hotkeys) do
|
||
|
for i=1,#val do
|
||
|
val[i]:disable()
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
if obj.trigger == nil then
|
||
|
obj.trigger = hs.hotkey.bind("", "tab", nil, function() obj:switchSource() end)
|
||
|
else
|
||
|
obj.trigger:enable()
|
||
|
end
|
||
|
for key,val in pairs(obj.hotkeys) do
|
||
|
if key == obj.source_kw then
|
||
|
for i=1,#val do
|
||
|
val[i]:enable()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
obj.chooser:show()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return obj
|