dotfiles/.zsh/zsh-you-should-use/you-should-use.plugin.zsh

285 lines
8.1 KiB
Bash
Raw Permalink Normal View History

2024-09-22 19:14:22 +03:00
#!/bin/zsh
export YSU_VERSION='1.9.0'
if ! type "tput" > /dev/null; then
printf "WARNING: tput command not found on your PATH.\n"
printf "zsh-you-should-use will fallback to uncoloured messages\n"
else
NONE="$(tput sgr0)"
BOLD="$(tput bold)"
RED="$(tput setaf 1)"
YELLOW="$(tput setaf 3)"
PURPLE="$(tput setaf 5)"
fi
function check_alias_usage() {
# Optional parameter that limits how far back history is checked
# I've chosen a large default value instead of bypassing tail because it's simpler
local limit="${1:-${HISTSIZE:-9000000000000000}}"
local key
declare -A usage
for key in "${(@k)aliases}"; do
usage[$key]=0
done
# TODO:
# Handle and (&&) + (&)
# others? watch, time etc...
local -a histfile_lines
histfile_lines=("${(@f)$(<$HISTFILE)}")
histfile_lines=("${histfile_lines[@]#*;}")
local current=0
local total=${#histfile_lines}
if [[ $total -gt $limit ]]; then
total=$limit
fi
local entry
for line in ${histfile_lines[@]} ; do
for entry in ${(@s/|/)line}; do
# Remove leading whitespace
entry=${entry##*[[:space:]]}
# We only care about the first word because that's all aliases work with
# (this does not count global and git aliases)
local word=${entry[(w)1]}
if [[ -n ${usage[$word]} ]]; then
(( usage[$word]++ ))
fi
done
# print current progress
(( current++ ))
printf "Analysing: [$current/$total]\r"
done
# Clear all previous line output
printf "\r\033[K"
# Print ordered usage
for key in ${(k)usage}; do
echo "${usage[$key]}: ${(q+)key}=${(q+)aliases[$key]}"
done | sort -rn -k1
}
# Writing to a buffer rather than directly to stdout/stderr allows us to decide
# if we want to write the reminder message before or after a command has been executed
function _write_ysu_buffer() {
_YSU_BUFFER+="$@"
# Maintain historical behaviour by default
local position="${YSU_MESSAGE_POSITION:-before}"
if [[ "$position" = "before" ]]; then
_flush_ysu_buffer
elif [[ "$position" != "after" ]]; then
(>&2 printf "${RED}${BOLD}Unknown value for YSU_MESSAGE_POSITION '$position'. ")
(>&2 printf "Expected value 'before' or 'after'${NONE}\n")
_flush_ysu_buffer
fi
}
function _flush_ysu_buffer() {
# It's important to pass $_YSU_BUFFER to printfs first argument
# because otherwise all escape codes will not printed correctly
(>&2 printf "$_YSU_BUFFER")
_YSU_BUFFER=""
}
function ysu_message() {
local DEFAULT_MESSAGE_FORMAT="${BOLD}${YELLOW}\
Found existing %alias_type for ${PURPLE}\"%command\"${YELLOW}. \
You should use: ${PURPLE}\"%alias\"${NONE}"
local alias_type_arg="${1}"
local command_arg="${2}"
local alias_arg="${3}"
# Escape arguments which will be interpreted by printf incorrectly
# unfortunately there does not seem to be a nice way to put this into
# a function because returning the values requires to be done by printf/echo!!
command_arg="${command_arg//\%/%%}"
command_arg="${command_arg//\\/\\\\}"
local MESSAGE="${YSU_MESSAGE_FORMAT:-"$DEFAULT_MESSAGE_FORMAT"}"
MESSAGE="${MESSAGE//\%alias_type/$alias_type_arg}"
MESSAGE="${MESSAGE//\%command/$command_arg}"
MESSAGE="${MESSAGE//\%alias/$alias_arg}"
_write_ysu_buffer "$MESSAGE\n"
}
# Prevent command from running if hardcore mode enabled
function _check_ysu_hardcore() {
if (( ${+YSU_HARDCORE} )); then
_write_ysu_buffer "${BOLD}${RED}You Should Use hardcore mode enabled. Use your aliases!${NONE}\n"
kill -s INT $$
fi
}
function _check_git_aliases() {
local typed="$1"
local expanded="$2"
# sudo will use another user's profile and so aliases would not apply
if [[ "$typed" = "sudo "* ]]; then
return
fi
if [[ "$typed" = "git "* ]]; then
local found=false
git config --get-regexp "^alias\..+$" | sort | while read key value; do
key="${key#alias.}"
# if for some reason, read does not split correctly, we
# detect that and manually split the key and value
if [[ -z "$value" ]]; then
value="${key#* }"
key="${key%% *}"
fi
if [[ "$expanded" = "git $value" || "$expanded" = "git $value "* ]]; then
ysu_message "git alias" "$value" "git $key"
found=true
fi
done
if $found; then
_check_ysu_hardcore
fi
fi
}
function _check_global_aliases() {
local typed="$1"
local expanded="$2"
local found=false
local tokens
local key
local value
local entry
# sudo will use another user's profile and so aliases would not apply
if [[ "$typed" = "sudo "* ]]; then
return
fi
alias -g | sort | while IFS="=" read -r key value; do
key="${key## }"
key="${key%% }"
value="${(Q)value}"
# Skip ignored global aliases
if [[ ${YSU_IGNORED_GLOBAL_ALIASES[(r)$key]} == "$key" ]]; then
continue
fi
if [[ "$typed" = *" $value "* || \
"$typed" = *" $value" || \
"$typed" = "$value "* || \
"$typed" = "$value" ]]; then
ysu_message "global alias" "$value" "$key"
found=true
fi
done
if $found; then
_check_ysu_hardcore
fi
}
function _check_aliases() {
local typed="$1"
local expanded="$2"
local found_aliases
found_aliases=()
local best_match=""
local best_match_value=""
local key
local value
# sudo will use another user's profile and so aliases would not apply
if [[ "$typed" = "sudo "* ]]; then
return
fi
# Find alias matches
for key in "${(@k)aliases}"; do
value="${aliases[$key]}"
# Skip ignored aliases
if [[ ${YSU_IGNORED_ALIASES[(r)$key]} == "$key" ]]; then
continue
fi
if [[ "$typed" = "$value" || "$typed" = "$value "* ]]; then
# if the alias longer or the same length as its command
# we assume that it is there to cater for typos.
# If not, then the alias would not save any time
# for the user and so doesn't hold much value anyway
if [[ "${#value}" -gt "${#key}" ]]; then
found_aliases+="$key"
# Match aliases to longest portion of command
if [[ "${#value}" -gt "${#best_match_value}" ]]; then
best_match="$key"
best_match_value="$value"
# on equal length, choose the shortest alias
elif [[ "${#value}" -eq "${#best_match}" && ${#key} -lt "${#best_match}" ]]; then
best_match="$key"
best_match_value="$value"
fi
fi
fi
done
# Print result matches based on current mode
if [[ "$YSU_MODE" = "ALL" ]]; then
for key in ${(@ok)found_aliases}; do
value="${aliases[$key]}"
ysu_message "alias" "$value" "$key"
done
elif [[ (-z "$YSU_MODE" || "$YSU_MODE" = "BESTMATCH") && -n "$best_match" ]]; then
# make sure that the best matched alias has not already
# been typed by the user
value="${aliases[$best_match]}"
if [[ "$typed" = "$best_match" || "$typed" = "$best_match "* ]]; then
return
fi
ysu_message "alias" "$value" "$best_match"
fi
if [[ -n "$found_aliases" ]]; then
_check_ysu_hardcore
fi
}
function disable_you_should_use() {
add-zsh-hook -D preexec _check_aliases
add-zsh-hook -D preexec _check_global_aliases
add-zsh-hook -D preexec _check_git_aliases
add-zsh-hook -D precmd _flush_ysu_buffer
}
function enable_you_should_use() {
disable_you_should_use # Delete any possible pre-existing hooks
add-zsh-hook preexec _check_aliases
add-zsh-hook preexec _check_global_aliases
add-zsh-hook preexec _check_git_aliases
add-zsh-hook precmd _flush_ysu_buffer
}
autoload -Uz add-zsh-hook
enable_you_should_use