Last active
March 28, 2025 11:30
-
-
Save dvessel/167a1dc16d14bc3563f5e546b21ebe1b to your computer and use it in GitHub Desktop.
search through ๐บ formulae/casks with fzf. `brew install fzf` first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env zsh | |
set -e | |
local pkgdef _pkgdef | |
zparseopts -D -E - {-formula,-formulae,-cask}=_pkgdef | |
# conditional output based on the homebrew package definition being worked on. | |
# example: `c.pkgdef output-for-formula output-for-cask` | |
# if `--formula` is passed to this script, `output-for-formula` will be output. | |
function c.pkgdef { | |
print -- ${${pkgdef/--formula/$1}/--cask/$2} | |
} | |
# will be either --formula or --cask. | |
pkgdef=${${_pkgdef:---formula}/--formulae/--formula} | |
local brew_commands_any=( | |
install info uses deps desc fetch cleanup options home homepage log | |
audit cat formula livecheck style unbottled unpack | |
) | |
local brew_commands_installed=( | |
reinstall uninstall remove rm outdated upgrade postinstall missing | |
list ls link ln unlink pin unpin edit migrate gist-logs bottle linkage test | |
--prefix --cellar --cache | |
) | |
local brew_commands=( $brew_commands_any $brew_commands_installed ) | |
local cask_incompatible=( | |
link ln unlink gist-logs migrate missing options bottle formula linkage test | |
pin unpin unbottled unpack --prefix --cellar | |
) | |
if [[ $pkgdef == --cask ]]; then | |
brew_commands=( ${brew_commands:|cask_incompatible} ) | |
fi | |
# Single select commands for fzf single select mode. | |
local no_multi=( log gist-logs migrate ) | |
local brew_args | |
if (( $brew_commands[(I)$1] )); then | |
brew_args=( $@ ) | |
elif [[ $1 == (--formula|--cask) && -z ${(@)argv:*brew_commands} || -z $@ ]]; then | |
brew_args=( info $@ ) | |
elif [[ -n ${(@)argv:*brew_commands} ]]; then | |
printf "Did you mean \`%s\`?\n" "${(@)argv:*brew_commands} $pkgdef" >&2 | |
return 1 | |
else | |
printf "Supported \`%s\` commands:\n%s [See brew --help]\n" \ | |
"$pkgdef" "${(j[, ])brew_commands}" >&2 | |
return 1 | |
fi | |
local names=`brew --cache`/api/`c.pkgdef formula_names cask_names` | |
local fzf_list=`brew --cache`/fzf${pkgdef}-list-`machine`.txt | |
# - delete fzf_list to rebuild listing. | |
# - save new formulae/casks names to present later in fzf. | |
# - delete the new names older than 3 days. | |
if [[ -f $names.$0:t-before.txt && -f $names.txt ]]; then | |
local new_keys=( `comm -1 -3 $names.$0:t-before.txt $names.txt` ) | |
local names_new=$names.$0:t-new-`print -- $new_keys[@] | md5`.txt | |
if [[ ${#new_keys} > 0 && ! -f $names_new ]]; then | |
printf "%s\n" $new_keys > $names_new | |
rm -f $fzf_list | |
fi | |
# clear new entries older than 3 days. | |
for f in $names.$0:t-new-*.txt(Nm+3); rm $f | |
fi | |
if [[ -f $names.txt ]]; then | |
cp -f $names.txt $names.$0:t-before.txt | |
fi | |
# hash the names of files holding the `new` state and dir listings that would affect the list. | |
local context=( | |
$names.${0:t}-new-*(N:t:r) | |
$(brew $(c.pkgdef --cellar --caskroom))/*(N:t) | |
) | |
if [[ $pkgdef == --formula ]]; then | |
context+=( `brew --prefix`/var/homebrew/pinned/*(N:t) ) | |
fi | |
local state=`print -- $context[@] | md5` | |
if [[ ! -f $fzf_list || $state != `sed '1!d' $fzf_list` ]]; then | |
typeset -aU deps reqs pins new | |
if [[ $pkgdef == --formula ]]; then | |
while read -r l ; do | |
case ${l/*:/} in | |
*dependency) deps+=${l/:*/} ;; | |
*request) reqs+=${l/:*/} ;; | |
esac | |
# calling `brew ls --installed-*` isn't cheap. combine calls and sort it out. | |
done < <( brew ls --installed-on-request --installed-as-dependency ) | |
pins=( `brew --prefix`/var/homebrew/pinned/*(N:t) ) | |
elif [[ $pkgdef == --cask ]]; then | |
reqs=( `brew ls --cask`) | |
fi | |
new=( `cat $names.$0:t-new-*.txt(N)` ) | |
# this works around regex capture limits in sed. limit each loop to 100 names. | |
# it can still fail if many names are very long, e.g., font migration to casks. | |
# it would be smarter to limit based on bytes. | |
local limit=100 | |
local maxcount=0 a=1 z=$limit | |
for n in ${#reqs} ${#deps} ${#new}; do | |
(( $n < $maxcount )) || maxcount=$n | |
done | |
local lsdesc=`brew desc $pkgdef --eval-all --name "/.+/"` | |
repeat `bc -le "ceil($(( $maxcount / $limit.0 )),0)"`; do | |
# ${xyz//\+/\\\\+} used to escape '+' which can be part of the name. | |
lsdesc=`print -- $lsdesc | \ | |
sed -uE "s/^(${(j[|])deps[$a,$z]//\+/\\\\+}):(.*)/\1:๐พ dependency.\2/" | \ | |
sed -uE "s/^(${(j[|])reqs[$a,$z]//\+/\\\\+}):(.*)/\1:๐บ installed.\2/" | \ | |
sed -uE "s/^(${(j[|])new[$a,$z]//\+/\\\\+}):(.*)/\1:โฐ new.\2/" | \ | |
sed -uE "s/^(${(j[|])pins[$a,$z]//\+/\\\\+}):(.*)/\1:๐\2/"` | |
a=$(( $a + $limit )) z=$(( $z + $limit )) | |
done | |
printf "%s\n%s\n" $state $lsdesc > $fzf_list | |
else | |
local lsdesc=`cat $fzf_list | sed 1,1d` | |
fi | |
case $brew_args[1] in | |
install) | |
# Omit installed and dependencies when installing. | |
lsdesc=`print -- $lsdesc | grep -vE "(๐บ installed|๐พ dependency)\."` | |
;; | |
uninstall) | |
# Can't uninstall a dependency. | |
lsdesc=`print -- $lsdesc | grep -vE "๐พ dependency\."` | |
;; | |
unpin) | |
# List only pinned formulae to unpin. | |
pins=( `brew --prefix`/var/homebrew/pinned/*(N:t) ) | |
if [[ ${#pins} == 0 ]]; then | |
print "Nothing to unpin." >&2 | |
return 1 | |
fi | |
lsdesc=`print -- $lsdesc | sed -nuE "/^(${(j[|])pins}):.*/p"` | |
;; | |
esac | |
# list installed formulae or casks for commands targeting them. | |
if (( $brew_commands_installed[(I)$brew_args[1]] )); then | |
typeset -a __lsdesc=( "==> Installed $(c.pkgdef Formulae Casks)" ) | |
while read -r l; do | |
if [[ ${l/*:/} =~ "^(๐|๐บ|๐พ).*" ]]; then | |
__lsdesc+=$l | |
fi | |
done < <( print -- $lsdesc ) | |
lsdesc=${(j[\n])__lsdesc} | |
fi | |
local selected=( | |
`print -- $lsdesc | fzf --header-lines=1 --prompt="brew $brew_args " \ | |
$( (( $no_multi[(I)$brew_args[1]] )) || printf --multi ) | grep -oE "^[^:]+"` | |
) | |
if [[ ! -z $selected ]]; then | |
# Write into history the command that would have been run directly. | |
# Formatted with `setopt extended_history` in mind. | |
printf ": %s:0;%s\n" `date -j +%s` "brew $brew_args $selected" >> $HISTFILE | |
printf "\033[4m%s\033[0m\n" "brew $brew_args $selected" | |
brew $brew_args $selected | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment