Skip to content

Instantly share code, notes, and snippets.

@dvessel
Last active March 28, 2025 11:30
Show Gist options
  • Save dvessel/167a1dc16d14bc3563f5e546b21ebe1b to your computer and use it in GitHub Desktop.
Save dvessel/167a1dc16d14bc3563f5e546b21ebe1b to your computer and use it in GitHub Desktop.
search through ๐Ÿบ formulae/casks with fzf. `brew install fzf` first.
#!/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