Files
hammerspoon/Spoons/BrewInfo.spoon/init.lua
T
2026-05-14 18:59:23 -04:00

196 lines
7.6 KiB
Lua

--- === BrewInfo ===
---
--- Display pop-up with Homebrew Formula info, or open their URL
---
--- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/BrewInfo.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/BrewInfo.spoon.zip)
---
--- You can bind keys to automatically display the output of `brew
--- info` of the currently-selected package name, or to open its
--- homepage. I use it to quickly explore new packages from the output
--- of `brew update`.
local mod={}
mod.__index = mod
-- Conformance hack, our Travis linter expects the object to be called "obj"
local obj=mod
-- Metadata
mod.name = "BrewInfo"
obj.version = "1.1"
mod.author = "Diego Zamboni <diego@zzamboni.org>"
mod.homepage = "https://github.com/Hammerspoon/Spoons"
mod.license = "MIT - https://opensource.org/licenses/MIT"
--- BrewInfo.brew_path
--- Variable
--- A string specifying the path to the `brew` executable. Defaults to `/usr/local/bin/brew`
mod.brew_path = "/usr/local/bin/brew"
--- BrewInfo.brew_info_delay_sec
--- Variable
--- An integer specifying how long the alerts generated by BrewInfo will stay onscreen
mod.brew_info_delay_sec = 3
--- BrewInfo.brew_info_style
--- Variable
--- A table in conformance with the [hs.alert.defaultStyle](http://www.hammerspoon.org/docs/hs.alert.html#defaultStyle[]) format that specifies the style used by the alerts. Default value: `{ textFont = "Courier New", textSize = 14, radius = 10 }`
mod.brew_info_style = {
textFont = "Courier New",
textSize = 14,
radius = 10
}
--- BrewInfo.select_text_if_needed
--- Variable
--- If `true`, and no text is currently selected in the terminal, issue a double-click to select the text below the cursor, and use that as the input to `brew info`. See also `BrewInfo.select_text_modifiers`. Defaults to `true`.
mod.select_text_if_needed = true
--- BrewInfo.select_text_modifiers
--- Variable
--- Table containing the modifiers to be used together with a double-click when `BrewInfo.select_text_if_needed` is true. Defaults to `{cmd = true, shift = true}` to issue a Cmd-Shift-double-click, which will select a continuous non-space string in Terminal and iTerm2.
mod.select_text_modifiers = {cmd = true, shift = true}
-- Internal function to issue a double click with given modifiers
function leftDoubleClick(modifiers)
local pos=hs.mouse.absolutePosition()
hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseDown, pos, modifiers)
:setProperty(hs.eventtap.event.properties.mouseEventClickState, 2)
:post()
hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseUp, pos, modifiers)
:post()
end
-- Internal method to get the currently selected text
-- If `select_text_if_needed` is true and no text is selected, issue
-- a double-click to select, then use that
function mod:current_selection()
local elem=hs.uielement.focusedElement()
if elem then
local sel = elem:selectedText()
if (sel == nil or sel == "") and self.select_text_if_needed then
-- Simulate a double click to select the text under the cursor
leftDoubleClick(self.select_text_modifiers)
hs.timer.usleep(20000)
sel = elem:selectedText()
end
return sel
else
return nil
end
end
-- Internal method to show an alert in the configured style
function mod:show(text)
hs.alert.show(text, self.brew_info_style, self.brew_info_delay_sec)
return self
end
--- BrewInfo:showBrewInfo(pkg, subcommand)
--- Method
--- Displays an alert with the output of `brew <subcommand> info <pkg>`
---
--- Parameters:
--- * pkg - name of the package to query
--- * subcommand - brew subcommand to use for the `info` command. Defaults to an empty string, which results in "brew info <pkg>" being run. For example, if `subcommand` is "cask", the `brew cask info <pkg>` command will be used.
---
--- Returns:
--- * The Spoon object
function mod:showBrewInfo(pkg, subcommand)
local info = "No package selected"
local st = nil
if pkg and pkg ~= "" then
local cmd=string.format("%s %s info %s", self.brew_path, subcommand or "", pkg)
info, st=hs.execute(cmd)
if st == nil then
info = "No information found about formula '" .. pkg .. "'!"
end
end
self:show(info)
return self
end
--- BrewInfo:showBrewInfoCurSel(subcommand)
--- Method
--- Display `brew <subcommand> info` using the selected text as the package name
---
--- Parameters:
--- * subcommand - brew subcommand to use for the `info` command. Defaults to an empty string, which results in "brew info" being run. For example, if `subcommand` is "cask", the `brew cask info` command will be used.
---
--- Returns:
--- * The Spoon object
function mod:showBrewInfoCurSel(subcommand)
return self:showBrewInfo(self:current_selection(), subcommand)
end
--- BrewInfo:openBrewURL(pkg, subcommand)
--- Method
--- Opens the homepage for package `pkg`, as obtained from the `homepage` field in `brew <subcommand> cat <pkg>`
---
--- Parameters:
--- * pkg - name of the package to query
--- * subcommand - brew subcommand to use for the `cat` command. Defaults to an empty string, which results in "brew cat <pkg>" being run. For example, if `subcommand` is "cask", the `brew cask cat <pkg>` command will be used.
---
--- Returns:
--- * The Spoon object
function mod:openBrewURL(pkg, subcommand)
local msg = "No package selected"
if pkg and pkg ~= "" then
local j, st, t, rc=hs.execute(string.format("%s %s cat %s", self.brew_path, (subcommand or ""), pkg ))
if st ~= nil then
local url=string.match(j, "\n%s*homepage%s+['\"](.-)['\"]%s*\n")
if url and url ~= "" then
hs.urlevent.openURLWithBundle(url, hs.urlevent.getDefaultHandler("http"))
return self
end
end
msg = "An error occurred obtaining information about '" .. pkg .. "'"
end
self:show(msg)
return self
end
--- BrewInfo:openBrewURLCurSel(subcommand)
--- Method
--- Opens the homepage for the currently-selected package, as obtained from the `homepage` field in `brew <subcommand> cat <pkg>`
---
--- Parameters:
--- * subcommand - brew subcommand to use for the `cat` command. Defaults to an empty string, which results in "brew cat <pkg>" being run. For example, if `subcommand` is "cask", the `brew cask cat <pkg>` command will be used.
---
--- Returns:
--- * The Spoon object
function mod:openBrewURLCurSel(subcommand)
return self:openBrewURL(self:current_selection(), subcommand)
end
--- BrewInfo:bindHotkeys(mapping)
--- Method
--- Binds hotkeys for BrewInfo
---
--- Parameters:
--- * mapping - A table containing hotkey modifier/key details for the following items:
--- * show_brew_info - Show output of `brew info` using the selected text as package name
--- * open_brew_url - Open the homepage of the formula whose name is currently selected
--- * show_brew_cask_info - Show output of `brew cask info` using the selected text as package name
--- * open_brew_cask_url - Open the homepage of the Cask whose name is currently selected
function mod:bindHotkeys(mapping)
local def = {
show_brew_info = function() self:showBrewInfoCurSel() end,
open_brew_url = function() self:openBrewURLCurSel() end,
}
for action, key in pairs(mapping) do
local subcommand_show = action:match("show_brew_(.*)_info")
if subcommand_show and subcommand_show ~= "" then
def[action] = function() self:showBrewInfoCurSel(subcommand_show) end
end
local subcommand_open = action:match("open_brew_(.*)_url")
if subcommand_open and subcommand_open ~= "" then
def[action] = function() self:openBrewURLCurSel(subcommand_open) end
end
end
hs.spoons.bindHotkeysToSpec(def, mapping)
end
return mod