mirror of
https://github.com/tw93/Mole.git
synced 2026-02-16 15:18:39 +00:00
feat: add shell completion script
This commit introduces a new `completion` command that provides shell completion for bash, zsh, and fish. The completion is implemented as a shell script in `bin/completion.sh` and the main `mole` script has been updated to use it. This approach was chosen to keep the completion logic in shell script, as the `mole` command is primarily a set of shell scripts.
This commit is contained in:
92
bin/completion.sh
Executable file
92
bin/completion.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
|
||||
case "$1" in
|
||||
bash)
|
||||
cat << 'EOF'
|
||||
_mole_completions()
|
||||
{
|
||||
local cur_word prev_word
|
||||
cur_word="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev_word="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
if [ "$COMP_CWORD" -eq 1 ]; then
|
||||
COMPREPLY=( $(compgen -W "optimize clean uninstall analyze status purge touchid update remove help version completion" -- "$cur_word") )
|
||||
else
|
||||
case "$prev_word" in
|
||||
completion)
|
||||
COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur_word") )
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F _mole_completions mole
|
||||
EOF
|
||||
;;
|
||||
zsh)
|
||||
cat << 'EOF'
|
||||
#compdef mole
|
||||
|
||||
_mole() {
|
||||
local -a subcommands
|
||||
subcommands=(
|
||||
'optimize:Free up disk space'
|
||||
'clean:Remove apps completely'
|
||||
'uninstall:Check and maintain system'
|
||||
'analyze:Explore disk usage'
|
||||
'status:Monitor system health'
|
||||
'purge:Remove old project artifacts'
|
||||
'touchid:Configure Touch ID for sudo'
|
||||
'update:Update to latest version'
|
||||
'remove:Remove Mole from system'
|
||||
'help:Show help'
|
||||
'version:Show version'
|
||||
'completion:Generate shell completions'
|
||||
)
|
||||
_describe 'subcommand' subcommands
|
||||
}
|
||||
|
||||
_mole
|
||||
EOF
|
||||
;;
|
||||
fish)
|
||||
cat << 'EOF'
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a optimize -d "Free up disk space"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a clean -d "Remove apps completely"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a uninstall -d "Check and maintain system"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a analyze -d "Explore disk usage"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a status -d "Monitor system health"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a purge -d "Remove old project artifacts"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a touchid -d "Configure Touch ID for sudo"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a update -d "Update to latest version"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a remove -d "Remove Mole from system"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a help -d "Show help"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a version -d "Show version"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a completion -d "Generate shell completions"
|
||||
|
||||
complete -c mole -n "not __fish_mole_no_subcommand" -a bash -d "generate bash completion" -n "__fish_see_subcommand_path completion"
|
||||
complete -c mole -n "not __fish_mole_no_subcommand" -a zsh -d "generate zsh completion" -n "__fish_see_subcommand_path completion"
|
||||
complete -c mole -n "not __fish_mole_no_subcommand" -a fish -d "generate fish completion" -n "__fish_see_subcommand_path completion"
|
||||
|
||||
function __fish_mole_no_subcommand
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i optimize clean uninstall analyze status purge touchid update remove help version completion
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_see_subcommand_path
|
||||
string match -q -- "completion" (commandline -opc)[1]
|
||||
end
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "Usage: mole completion [bash|zsh|fish]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
82
mole
82
mole
@@ -17,19 +17,19 @@ MOLE_TAGLINE="Deep clean and optimize your Mac."
|
||||
|
||||
is_touchid_configured() {
|
||||
local pam_sudo_file="/etc/pam.d/sudo"
|
||||
[[ -f "$pam_sudo_file" ]] && grep -q "pam_tid.so" "$pam_sudo_file" 2> /dev/null
|
||||
[[ -f "$pam_sudo_file" ]] && grep -q "pam_tid.so" "$pam_sudo_file" 2>/dev/null
|
||||
}
|
||||
|
||||
get_latest_version() {
|
||||
curl -fsSL --connect-timeout 2 --max-time 3 -H "Cache-Control: no-cache" \
|
||||
"https://raw.githubusercontent.com/tw93/mole/main/mole" 2> /dev/null |
|
||||
"https://raw.githubusercontent.com/tw93/mole/main/mole" 2>/dev/null |
|
||||
grep '^VERSION=' | head -1 | sed 's/VERSION="\(.*\)"/\1/'
|
||||
}
|
||||
|
||||
get_latest_version_from_github() {
|
||||
local version
|
||||
version=$(curl -fsSL --connect-timeout 2 --max-time 3 \
|
||||
"https://api.github.com/repos/tw93/mole/releases/latest" 2> /dev/null |
|
||||
"https://api.github.com/repos/tw93/mole/releases/latest" 2>/dev/null |
|
||||
grep '"tag_name"' | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
|
||||
version="${version#v}"
|
||||
version="${version#V}"
|
||||
@@ -39,11 +39,11 @@ get_latest_version_from_github() {
|
||||
# Install detection (Homebrew vs manual).
|
||||
is_homebrew_install() {
|
||||
local mole_path
|
||||
mole_path=$(command -v mole 2> /dev/null) || return 1
|
||||
mole_path=$(command -v mole 2>/dev/null) || return 1
|
||||
|
||||
if [[ -L "$mole_path" ]] && readlink "$mole_path" | grep -q "Cellar/mole"; then
|
||||
if command -v brew > /dev/null 2>&1; then
|
||||
brew list --formula 2> /dev/null | grep -q "^mole$" && return 0
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
brew list --formula 2>/dev/null | grep -q "^mole$" && return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
@@ -53,8 +53,8 @@ is_homebrew_install() {
|
||||
case "$mole_path" in
|
||||
/opt/homebrew/bin/mole | /usr/local/bin/mole)
|
||||
if [[ -d /opt/homebrew/Cellar/mole ]] || [[ -d /usr/local/Cellar/mole ]]; then
|
||||
if command -v brew > /dev/null 2>&1; then
|
||||
brew list --formula 2> /dev/null | grep -q "^mole$" && return 0
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
brew list --formula 2>/dev/null | grep -q "^mole$" && return 0
|
||||
else
|
||||
return 0 # Cellar exists, probably Homebrew install
|
||||
fi
|
||||
@@ -63,11 +63,11 @@ is_homebrew_install() {
|
||||
esac
|
||||
fi
|
||||
|
||||
if command -v brew > /dev/null 2>&1; then
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
local brew_prefix
|
||||
brew_prefix=$(brew --prefix 2> /dev/null)
|
||||
brew_prefix=$(brew --prefix 2>/dev/null)
|
||||
if [[ -n "$brew_prefix" && "$mole_path" == "$brew_prefix/bin/mole" && -d "$brew_prefix/Cellar/mole" ]]; then
|
||||
brew list --formula 2> /dev/null | grep -q "^mole$" && return 0
|
||||
brew list --formula 2>/dev/null | grep -q "^mole$" && return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -89,12 +89,12 @@ check_for_updates() {
|
||||
fi
|
||||
|
||||
if [[ -n "$latest" && "$VERSION" != "$latest" && "$(printf '%s\n' "$VERSION" "$latest" | sort -V | head -1)" == "$VERSION" ]]; then
|
||||
printf "\nUpdate available: %s → %s, run %smo update%s\n\n" "$VERSION" "$latest" "$GREEN" "$NC" > "$msg_cache"
|
||||
printf "\nUpdate available: %s → %s, run %smo update%s\n\n" "$VERSION" "$latest" "$GREEN" "$NC" >"$msg_cache"
|
||||
else
|
||||
echo -n > "$msg_cache"
|
||||
echo -n >"$msg_cache"
|
||||
fi
|
||||
) &
|
||||
disown 2> /dev/null || true
|
||||
disown 2>/dev/null || true
|
||||
}
|
||||
|
||||
show_update_notification() {
|
||||
@@ -107,7 +107,7 @@ show_update_notification() {
|
||||
|
||||
# UI helpers
|
||||
show_brand_banner() {
|
||||
cat << EOF
|
||||
cat <<EOF
|
||||
${GREEN} __ __ _ ${NC}
|
||||
${GREEN}| \/ | ___ | | ___ ${NC}
|
||||
${GREEN}| |\/| |/ _ \| |/ _ \\${NC}
|
||||
@@ -131,7 +131,7 @@ animate_mole_intro() {
|
||||
if is_christmas_season; then
|
||||
while IFS= read -r line; do
|
||||
mole_lines+=("$line")
|
||||
done << 'EOF'
|
||||
done <<'EOF'
|
||||
*
|
||||
/o\
|
||||
{/\_/\}
|
||||
@@ -145,7 +145,7 @@ EOF
|
||||
else
|
||||
while IFS= read -r line; do
|
||||
mole_lines+=("$line")
|
||||
done << 'EOF'
|
||||
done <<'EOF'
|
||||
/\_/\
|
||||
____/ o o \
|
||||
/~____ =o= /
|
||||
@@ -195,7 +195,7 @@ EOF
|
||||
|
||||
show_version() {
|
||||
local os_ver
|
||||
if command -v sw_vers > /dev/null; then
|
||||
if command -v sw_vers >/dev/null; then
|
||||
os_ver=$(sw_vers -productVersion)
|
||||
else
|
||||
os_ver="Unknown"
|
||||
@@ -208,15 +208,15 @@ show_version() {
|
||||
kernel=$(uname -r)
|
||||
|
||||
local sip_status
|
||||
if command -v csrutil > /dev/null; then
|
||||
sip_status=$(csrutil status 2> /dev/null | grep -o "enabled\|disabled" || echo "Unknown")
|
||||
sip_status="$(tr '[:lower:]' '[:upper:]' <<< "${sip_status:0:1}")${sip_status:1}"
|
||||
if command -v csrutil >/dev/null; then
|
||||
sip_status=$(csrutil status 2>/dev/null | grep -o "enabled\|disabled" || echo "Unknown")
|
||||
sip_status="$(tr '[:lower:]' '[:upper:]' <<<"${sip_status:0:1}")${sip_status:1}"
|
||||
else
|
||||
sip_status="Unknown"
|
||||
fi
|
||||
|
||||
local disk_free
|
||||
disk_free=$(df -h / 2> /dev/null | awk 'NR==2 {print $4}' || echo "Unknown")
|
||||
disk_free=$(df -h / 2>/dev/null | awk 'NR==2 {print $4}' || echo "Unknown")
|
||||
|
||||
local install_method="Manual"
|
||||
if is_homebrew_install; then
|
||||
@@ -257,6 +257,8 @@ show_help() {
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo optimize --whitelist" "$NC" "Manage protected items"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo purge --paths" "$NC" "Configure scan directories"
|
||||
echo
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo completion" "$NC" "Generate shell completion"
|
||||
echo
|
||||
printf "%s%s%s\n" "$BLUE" "OPTIONS" "$NC"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "--debug" "$NC" "Show detailed operation logs"
|
||||
echo
|
||||
@@ -304,7 +306,7 @@ update_mole() {
|
||||
}
|
||||
|
||||
local download_error=""
|
||||
if command -v curl > /dev/null 2>&1; then
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
download_error=$(curl -fsSL --connect-timeout 10 --max-time 60 "$installer_url" -o "$tmp_installer" 2>&1) || {
|
||||
local curl_exit=$?
|
||||
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
||||
@@ -321,7 +323,7 @@ update_mole() {
|
||||
echo -e "${YELLOW}Tip:${NC} URL: $installer_url"
|
||||
exit 1
|
||||
}
|
||||
elif command -v wget > /dev/null 2>&1; then
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
download_error=$(wget --timeout=10 --tries=3 -qO "$tmp_installer" "$installer_url" 2>&1) || {
|
||||
if [[ -t 1 ]]; then stop_inline_spinner; fi
|
||||
rm -f "$tmp_installer"
|
||||
@@ -342,7 +344,7 @@ update_mole() {
|
||||
chmod +x "$tmp_installer"
|
||||
|
||||
local mole_path
|
||||
mole_path="$(command -v mole 2> /dev/null || echo "$0")"
|
||||
mole_path="$(command -v mole 2>/dev/null || echo "$0")"
|
||||
local install_dir
|
||||
install_dir="$(cd "$(dirname "$mole_path")" && pwd)"
|
||||
|
||||
@@ -379,7 +381,7 @@ update_mole() {
|
||||
|
||||
if ! printf '%s\n' "$output" | grep -Eq "Updated to latest version|Already on latest version"; then
|
||||
local new_version
|
||||
new_version=$("$mole_path" --version 2> /dev/null | awk 'NF {print $NF}' || echo "")
|
||||
new_version=$("$mole_path" --version 2>/dev/null | awk 'NF {print $NF}' || echo "")
|
||||
printf '\n%s\n\n' "${GREEN}${ICON_SUCCESS}${NC} Updated to latest version (${new_version:-unknown})"
|
||||
else
|
||||
printf '\n'
|
||||
@@ -424,7 +426,7 @@ remove_mole() {
|
||||
local -a manual_installs=()
|
||||
local -a alias_installs=()
|
||||
|
||||
if command -v brew > /dev/null 2>&1; then
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
brew_cmd="brew"
|
||||
elif [[ -x "/opt/homebrew/bin/brew" ]]; then
|
||||
brew_cmd="/opt/homebrew/bin/brew"
|
||||
@@ -433,7 +435,7 @@ remove_mole() {
|
||||
fi
|
||||
|
||||
if [[ -n "$brew_cmd" ]]; then
|
||||
if "$brew_cmd" list --formula 2> /dev/null | grep -q "^mole$"; then
|
||||
if "$brew_cmd" list --formula 2>/dev/null | grep -q "^mole$"; then
|
||||
brew_has_mole="true"
|
||||
fi
|
||||
fi
|
||||
@@ -443,7 +445,7 @@ remove_mole() {
|
||||
fi
|
||||
|
||||
local found_mole
|
||||
found_mole=$(command -v mole 2> /dev/null || true)
|
||||
found_mole=$(command -v mole 2>/dev/null || true)
|
||||
if [[ -n "$found_mole" && -f "$found_mole" ]]; then
|
||||
if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then
|
||||
manual_installs+=("$found_mole")
|
||||
@@ -465,7 +467,7 @@ remove_mole() {
|
||||
done
|
||||
|
||||
local found_mo
|
||||
found_mo=$(command -v mo 2> /dev/null || true)
|
||||
found_mo=$(command -v mo 2>/dev/null || true)
|
||||
if [[ -n "$found_mo" && -f "$found_mo" ]]; then
|
||||
alias_installs+=("$found_mo")
|
||||
fi
|
||||
@@ -544,11 +546,11 @@ remove_mole() {
|
||||
for install in "${manual_installs[@]}"; do
|
||||
if [[ -f "$install" ]]; then
|
||||
if [[ ! -w "$(dirname "$install")" ]]; then
|
||||
if ! sudo rm -f "$install" 2> /dev/null; then
|
||||
if ! sudo rm -f "$install" 2>/dev/null; then
|
||||
has_error=true
|
||||
fi
|
||||
else
|
||||
if ! rm -f "$install" 2> /dev/null; then
|
||||
if ! rm -f "$install" 2>/dev/null; then
|
||||
has_error=true
|
||||
fi
|
||||
fi
|
||||
@@ -559,11 +561,11 @@ remove_mole() {
|
||||
for alias in "${alias_installs[@]}"; do
|
||||
if [[ -f "$alias" ]]; then
|
||||
if [[ ! -w "$(dirname "$alias")" ]]; then
|
||||
if ! sudo rm -f "$alias" 2> /dev/null; then
|
||||
if ! sudo rm -f "$alias" 2>/dev/null; then
|
||||
has_error=true
|
||||
fi
|
||||
else
|
||||
if ! rm -f "$alias" 2> /dev/null; then
|
||||
if ! rm -f "$alias" 2>/dev/null; then
|
||||
has_error=true
|
||||
fi
|
||||
fi
|
||||
@@ -571,10 +573,10 @@ remove_mole() {
|
||||
done
|
||||
fi
|
||||
if [[ -d "$HOME/.cache/mole" ]]; then
|
||||
rm -rf "$HOME/.cache/mole" 2> /dev/null || true
|
||||
rm -rf "$HOME/.cache/mole" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -d "$HOME/.config/mole" ]]; then
|
||||
rm -rf "$HOME/.config/mole" 2> /dev/null || true
|
||||
rm -rf "$HOME/.config/mole" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
local final_message
|
||||
@@ -607,12 +609,12 @@ show_main_menu() {
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
printf '\r\033[2K%s\n' "$line"
|
||||
done <<< "$banner"
|
||||
done <<<"$banner"
|
||||
|
||||
if [[ -n "$update_message" ]]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
printf '\r\033[2K%s\n' "$line"
|
||||
done <<< "$update_message"
|
||||
done <<<"$update_message"
|
||||
fi
|
||||
|
||||
printf '\r\033[2K\n'
|
||||
@@ -642,7 +644,7 @@ show_main_menu() {
|
||||
interactive_main_menu() {
|
||||
if [[ -t 1 ]]; then
|
||||
local tty_name
|
||||
tty_name=$(tty 2> /dev/null || echo "")
|
||||
tty_name=$(tty 2>/dev/null || echo "")
|
||||
if [[ -n "$tty_name" ]]; then
|
||||
local flag_file
|
||||
local cache_dir="$HOME/.cache/mole"
|
||||
@@ -664,7 +666,7 @@ interactive_main_menu() {
|
||||
MAIN_MENU_BANNER="$brand_banner"
|
||||
|
||||
if [[ -f "$msg_cache" && -s "$msg_cache" ]]; then
|
||||
update_message="$(cat "$msg_cache" 2> /dev/null || echo "")"
|
||||
update_message="$(cat "$msg_cache" 2>/dev/null || echo "")"
|
||||
fi
|
||||
MAIN_MENU_UPDATE_MESSAGE="$update_message"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user