Dotfiles update 2025-12-22 12:00
This commit is contained in:
@@ -8,164 +8,73 @@
|
||||
# - Suggests existing aliases for frequently typed commands
|
||||
# - "Did you mean?" for unknown commands
|
||||
# - Package installation suggestions for missing commands
|
||||
#
|
||||
# Add to .zshrc:
|
||||
# source ~/.dotfiles/zsh/functions/smart-suggest.zsh
|
||||
# ============================================================================
|
||||
|
||||
# Source shared colors (with fallback)
|
||||
source "${0:A:h}/../lib/colors.zsh" 2>/dev/null || \
|
||||
source "$HOME/.dotfiles/zsh/lib/colors.zsh" 2>/dev/null || {
|
||||
typeset -g DF_CYAN=$'\033[0;36m' DF_YELLOW=$'\033[1;33m'
|
||||
typeset -g DF_GREEN=$'\033[0;32m' DF_RED=$'\033[0;31m'
|
||||
typeset -g DF_DIM=$'\033[2m' DF_NC=$'\033[0m'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Enable/disable features
|
||||
typeset -g SMART_SUGGEST_ENABLED=true
|
||||
typeset -g SMART_SUGGEST_TYPOS=true
|
||||
typeset -g SMART_SUGGEST_ALIASES=true
|
||||
typeset -g SMART_SUGGEST_PACKAGES=true
|
||||
typeset -g SMART_SUGGEST_HISTORY=true
|
||||
|
||||
# Tracking file for alias suggestions
|
||||
typeset -g SMART_SUGGEST_TRACK_FILE="$HOME/.cache/smart-suggest-track"
|
||||
|
||||
# Colors
|
||||
typeset -g SS_CYAN=$'\033[0;36m'
|
||||
typeset -g SS_YELLOW=$'\033[1;33m'
|
||||
typeset -g SS_GREEN=$'\033[0;32m'
|
||||
typeset -g SS_RED=$'\033[0;31m'
|
||||
typeset -g SS_DIM=$'\033[2m'
|
||||
typeset -g SS_NC=$'\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# Common Typo Database
|
||||
# ============================================================================
|
||||
|
||||
typeset -gA TYPO_CORRECTIONS=(
|
||||
# Git typos
|
||||
[gti]="git"
|
||||
[gitt]="git"
|
||||
[got]="git"
|
||||
[gut]="git"
|
||||
[gi]="git"
|
||||
[giit]="git"
|
||||
[ggit]="git"
|
||||
[gitst]="git st"
|
||||
[gits]="git s"
|
||||
[gitl]="git l"
|
||||
[gitd]="git d"
|
||||
[gitp]="git p"
|
||||
[psuh]="push"
|
||||
[psull]="pull"
|
||||
[pul]="pull"
|
||||
[puhs]="push"
|
||||
[stauts]="status"
|
||||
[statis]="status"
|
||||
[statuus]="status"
|
||||
[comit]="commit"
|
||||
[commti]="commit"
|
||||
[commt]="commit"
|
||||
[chekcout]="checkout"
|
||||
[chekout]="checkout"
|
||||
[checkou]="checkout"
|
||||
[branhc]="branch"
|
||||
[barnch]="branch"
|
||||
[bracnh]="branch"
|
||||
[marge]="merge"
|
||||
[merg]="merge"
|
||||
[stsh]="stash"
|
||||
[stahs]="stash"
|
||||
[gti]="git" [gitt]="git" [got]="git" [gut]="git" [gi]="git"
|
||||
[giit]="git" [ggit]="git" [gitst]="git st" [gits]="git s"
|
||||
[gitl]="git l" [gitd]="git d" [gitp]="git p"
|
||||
[psuh]="push" [psull]="pull" [pul]="pull" [puhs]="push"
|
||||
[stauts]="status" [statis]="status" [statuus]="status"
|
||||
[comit]="commit" [commti]="commit" [commt]="commit"
|
||||
[chekcout]="checkout" [chekout]="checkout" [checkou]="checkout"
|
||||
[branhc]="branch" [barnch]="branch" [bracnh]="branch"
|
||||
[marge]="merge" [merg]="merge" [stsh]="stash" [stahs]="stash"
|
||||
|
||||
# Docker typos
|
||||
[dokcer]="docker"
|
||||
[doker]="docker"
|
||||
[docekr]="docker"
|
||||
[dcoker]="docker"
|
||||
[dockr]="docker"
|
||||
[docke]="docker"
|
||||
[docker-compoes]="docker-compose"
|
||||
[docker-compsoe]="docker-compose"
|
||||
[dokcer]="docker" [doker]="docker" [docekr]="docker"
|
||||
[dcoker]="docker" [dockr]="docker" [docke]="docker"
|
||||
[docker-compoes]="docker-compose" [docker-compsoe]="docker-compose"
|
||||
[dokcer-compose]="docker-compose"
|
||||
|
||||
# Common command typos
|
||||
[sl]="ls"
|
||||
[l]="ls"
|
||||
[sls]="ls"
|
||||
[lss]="ls"
|
||||
[cta]="cat"
|
||||
[catt]="cat"
|
||||
[caat]="cat"
|
||||
[grpe]="grep"
|
||||
[gerp]="grep"
|
||||
[gre]="grep"
|
||||
[grepp]="grep"
|
||||
[mkdri]="mkdir"
|
||||
[mkdr]="mkdir"
|
||||
[mdkir]="mkdir"
|
||||
[mdir]="mkdir"
|
||||
[rn]="rm"
|
||||
[rmm]="rm"
|
||||
[chmdo]="chmod"
|
||||
[chomd]="chmod"
|
||||
[chonw]="chown"
|
||||
[cown]="chown"
|
||||
[tarr]="tar"
|
||||
[tart]="tar"
|
||||
[wegt]="wget"
|
||||
[wgte]="wget"
|
||||
[weget]="wget"
|
||||
[crul]="curl"
|
||||
[crul]="curl"
|
||||
[curll]="curl"
|
||||
[pytohn]="python"
|
||||
[pyhton]="python"
|
||||
[pythn]="python"
|
||||
[pyton]="python"
|
||||
[pthon]="python"
|
||||
[pytho]="python"
|
||||
[ndoe]="node"
|
||||
[noed]="node"
|
||||
[noode]="node"
|
||||
[npn]="npm"
|
||||
[nmpm]="npm"
|
||||
[nppm]="npm"
|
||||
[yran]="yarn"
|
||||
[yaarn]="yarn"
|
||||
[yanr]="yarn"
|
||||
[suod]="sudo"
|
||||
[sudi]="sudo"
|
||||
[sduo]="sudo"
|
||||
[sudoo]="sudo"
|
||||
[sssh]="ssh"
|
||||
[shh]="ssh"
|
||||
[sssh]="ssh"
|
||||
[scpp]="scp"
|
||||
[spcp]="scp"
|
||||
[vmi]="vim"
|
||||
[imv]="vim"
|
||||
[viim]="vim"
|
||||
[cde]="code"
|
||||
[cdoe]="code"
|
||||
[cod]="code"
|
||||
[clera]="clear"
|
||||
[cler]="clear"
|
||||
[claer]="clear"
|
||||
[ecoh]="echo"
|
||||
[ehco]="echo"
|
||||
[echoo]="echo"
|
||||
[exti]="exit"
|
||||
[ext]="exit"
|
||||
[exitt]="exit"
|
||||
[eixt]="exit"
|
||||
[histroy]="history"
|
||||
[hisotry]="history"
|
||||
[hsitory]="history"
|
||||
[histrory]="history"
|
||||
[maek]="make"
|
||||
[mkae]="make"
|
||||
[amke]="make"
|
||||
[makee]="make"
|
||||
[ccd]="cd"
|
||||
[cdd]="cd"
|
||||
[ccd]="cd"
|
||||
[sl]="ls" [l]="ls" [sls]="ls" [lss]="ls"
|
||||
[cta]="cat" [catt]="cat" [caat]="cat"
|
||||
[grpe]="grep" [gerp]="grep" [gre]="grep" [grepp]="grep"
|
||||
[mkdri]="mkdir" [mkdr]="mkdir" [mdkir]="mkdir" [mdir]="mkdir"
|
||||
[rn]="rm" [rmm]="rm" [chmdo]="chmod" [chomd]="chmod"
|
||||
[chonw]="chown" [cown]="chown" [tarr]="tar" [tart]="tar"
|
||||
[wegt]="wget" [wgte]="wget" [weget]="wget"
|
||||
[crul]="curl" [curll]="curl"
|
||||
[pytohn]="python" [pyhton]="python" [pythn]="python"
|
||||
[pyton]="python" [pthon]="python" [pytho]="python"
|
||||
[ndoe]="node" [noed]="node" [noode]="node"
|
||||
[npn]="npm" [nmpm]="npm" [nppm]="npm"
|
||||
[yran]="yarn" [yaarn]="yarn" [yanr]="yarn"
|
||||
[suod]="sudo" [sudi]="sudo" [sduo]="sudo" [sudoo]="sudo"
|
||||
[sssh]="ssh" [shh]="ssh" [scpp]="scp" [spcp]="scp"
|
||||
[vmi]="vim" [imv]="vim" [viim]="vim"
|
||||
[cde]="code" [cdoe]="code" [cod]="code"
|
||||
[clera]="clear" [cler]="clear" [claer]="clear"
|
||||
[ecoh]="echo" [ehco]="echo" [echoo]="echo"
|
||||
[exti]="exit" [ext]="exit" [exitt]="exit" [eixt]="exit"
|
||||
[histroy]="history" [hisotry]="history" [hsitory]="history"
|
||||
[histrory]="history" [maek]="make" [mkae]="make"
|
||||
[amke]="make" [makee]="make" [ccd]="cd" [cdd]="cd"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
@@ -173,46 +82,25 @@ typeset -gA TYPO_CORRECTIONS=(
|
||||
# ============================================================================
|
||||
|
||||
_ss_get_package_manager() {
|
||||
if command -v apt-get &>/dev/null; then
|
||||
echo "apt"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
echo "dnf"
|
||||
elif command -v pacman &>/dev/null; then
|
||||
echo "pacman"
|
||||
elif command -v brew &>/dev/null; then
|
||||
echo "brew"
|
||||
else
|
||||
echo ""
|
||||
if command -v apt-get &>/dev/null; then echo "apt"
|
||||
elif command -v dnf &>/dev/null; then echo "dnf"
|
||||
elif command -v pacman &>/dev/null; then echo "pacman"
|
||||
elif command -v brew &>/dev/null; then echo "brew"
|
||||
else echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Common commands and their packages
|
||||
typeset -gA COMMAND_PACKAGES=(
|
||||
[htop]="htop"
|
||||
[tree]="tree"
|
||||
[jq]="jq"
|
||||
[fd]="fd-find:apt fd:pacman fd:brew"
|
||||
[rg]="ripgrep"
|
||||
[bat]="bat"
|
||||
[eza]="eza"
|
||||
[exa]="exa"
|
||||
[fzf]="fzf"
|
||||
[tldr]="tldr"
|
||||
[ncdu]="ncdu"
|
||||
[duf]="duf"
|
||||
[dust]="dust"
|
||||
[procs]="procs"
|
||||
[bottom]="bottom"
|
||||
[btm]="bottom"
|
||||
[lazygit]="lazygit"
|
||||
[lazydocker]="lazydocker"
|
||||
[neofetch]="neofetch"
|
||||
[fastfetch]="fastfetch"
|
||||
[httpie]="httpie"
|
||||
[http]="httpie"
|
||||
[htop]="htop" [tree]="tree" [jq]="jq"
|
||||
[fd]="fd-find:apt fd:pacman fd:brew" [rg]="ripgrep"
|
||||
[bat]="bat" [eza]="eza" [exa]="exa" [fzf]="fzf"
|
||||
[tldr]="tldr" [ncdu]="ncdu" [duf]="duf" [dust]="dust"
|
||||
[procs]="procs" [bottom]="bottom" [btm]="bottom"
|
||||
[lazygit]="lazygit" [lazydocker]="lazydocker"
|
||||
[neofetch]="neofetch" [fastfetch]="fastfetch"
|
||||
[httpie]="httpie" [http]="httpie"
|
||||
[delta]="git-delta:apt delta:pacman git-delta:brew"
|
||||
[glow]="glow"
|
||||
[navi]="navi"
|
||||
[glow]="glow" [navi]="navi"
|
||||
)
|
||||
|
||||
_ss_suggest_package() {
|
||||
@@ -226,9 +114,7 @@ _ss_suggest_package() {
|
||||
|
||||
local pkg=""
|
||||
|
||||
# Check for PM-specific package name
|
||||
if [[ "$pkg_info" == *":"* ]]; then
|
||||
# Format: "pkg1:pm1 pkg2:pm2"
|
||||
for entry in ${(s: :)pkg_info}; do
|
||||
local p="${entry%%:*}"
|
||||
local m="${entry##*:}"
|
||||
@@ -237,7 +123,6 @@ _ss_suggest_package() {
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Fallback to first package
|
||||
[[ -z "$pkg" ]] && pkg="${${(s: :)pkg_info}[1]%%:*}"
|
||||
else
|
||||
pkg="$pkg_info"
|
||||
@@ -264,12 +149,11 @@ _ss_track_command() {
|
||||
[[ "$SMART_SUGGEST_ALIASES" != true ]] && return
|
||||
|
||||
local cmd="$1"
|
||||
[[ ${#cmd} -lt 8 ]] && return # Skip short commands
|
||||
[[ ${#cmd} -lt 8 ]] && return
|
||||
|
||||
mkdir -p "$(dirname "$SMART_SUGGEST_TRACK_FILE")"
|
||||
echo "$cmd" >> "$SMART_SUGGEST_TRACK_FILE"
|
||||
|
||||
# Periodically check for alias suggestions
|
||||
local count=$(grep -Fc "$cmd" "$SMART_SUGGEST_TRACK_FILE" 2>/dev/null || echo 0)
|
||||
|
||||
if [[ $count -ge 10 && $((count % 10)) -eq 0 ]]; then
|
||||
@@ -281,23 +165,21 @@ _ss_suggest_alias_for() {
|
||||
local cmd="$1"
|
||||
local count="$2"
|
||||
|
||||
# Check if an alias already exists for this command
|
||||
local existing=$(alias | grep -F "='$cmd'" | head -1 | cut -d= -f1)
|
||||
|
||||
if [[ -n "$existing" ]]; then
|
||||
echo
|
||||
echo -e "${SS_CYAN}💡 Tip:${SS_NC} You've typed '${SS_YELLOW}$cmd${SS_NC}' $count times"
|
||||
echo -e " You already have an alias: ${SS_GREEN}$existing${SS_NC}"
|
||||
echo -e "${DF_CYAN}💡 Tip:${DF_NC} You've typed '${DF_YELLOW}$cmd${DF_NC}' $count times"
|
||||
echo -e " You already have an alias: ${DF_GREEN}$existing${DF_NC}"
|
||||
else
|
||||
# Generate suggested alias name
|
||||
local suggested=$(echo "$cmd" | awk '{
|
||||
for(i=1; i<=NF && i<=3; i++)
|
||||
printf substr($i,1,1)
|
||||
}')
|
||||
|
||||
echo
|
||||
echo -e "${SS_CYAN}💡 Tip:${SS_NC} You've typed '${SS_YELLOW}$cmd${SS_NC}' $count times"
|
||||
echo -e " Consider adding: ${SS_GREEN}alias $suggested='$cmd'${SS_NC}"
|
||||
echo -e "${DF_CYAN}💡 Tip:${DF_NC} You've typed '${DF_YELLOW}$cmd${DF_NC}' $count times"
|
||||
echo -e " Consider adding: ${DF_GREEN}alias $suggested='$cmd'${DF_NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -315,34 +197,31 @@ command_not_found_handler() {
|
||||
return 127
|
||||
}
|
||||
|
||||
echo -e "${SS_RED}✗${SS_NC} Command not found: ${SS_YELLOW}$cmd${SS_NC}"
|
||||
echo -e "${DF_RED}✗${DF_NC} Command not found: ${DF_YELLOW}$cmd${DF_NC}"
|
||||
|
||||
local suggestion_made=false
|
||||
|
||||
# Check for typo
|
||||
if [[ "$SMART_SUGGEST_TYPOS" == true ]]; then
|
||||
local correction="${TYPO_CORRECTIONS[$cmd]}"
|
||||
if [[ -n "$correction" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} Did you mean: ${SS_GREEN}$correction${SS_NC}?"
|
||||
echo -e " ${SS_DIM}Run: $correction $args${SS_NC}"
|
||||
echo -e "${DF_CYAN}→${DF_NC} Did you mean: ${DF_GREEN}$correction${DF_NC}?"
|
||||
echo -e " ${DF_DIM}Run: $correction $args${DF_NC}"
|
||||
suggestion_made=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for similar commands
|
||||
if [[ "$suggestion_made" != true ]]; then
|
||||
local similar=$(compgen -c 2>/dev/null | grep -i "^${cmd:0:3}" | head -3 | tr '\n' ', ' | sed 's/,$//')
|
||||
if [[ -n "$similar" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} Similar commands: ${SS_GREEN}$similar${SS_NC}"
|
||||
echo -e "${DF_CYAN}→${DF_NC} Similar commands: ${DF_GREEN}$similar${DF_NC}"
|
||||
suggestion_made=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Suggest package installation
|
||||
if [[ "$SMART_SUGGEST_PACKAGES" == true ]]; then
|
||||
local install_cmd=$(_ss_suggest_package "$cmd")
|
||||
if [[ -n "$install_cmd" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} To install: ${SS_GREEN}$install_cmd${SS_NC}"
|
||||
echo -e "${DF_CYAN}→${DF_NC} To install: ${DF_GREEN}$install_cmd${DF_NC}"
|
||||
suggestion_made=true
|
||||
fi
|
||||
fi
|
||||
@@ -351,42 +230,30 @@ command_not_found_handler() {
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Pre-exec Hook for Tracking
|
||||
# Hooks
|
||||
# ============================================================================
|
||||
|
||||
_ss_preexec_hook() {
|
||||
local cmd="$1"
|
||||
|
||||
# Extract just the command (first word)
|
||||
local first_word="${cmd%% *}"
|
||||
|
||||
# Track full commands for alias suggestions
|
||||
_ss_track_command "$cmd"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Post-command Hook for Suggestions
|
||||
# ============================================================================
|
||||
|
||||
_ss_precmd_hook() {
|
||||
local exit_code=$?
|
||||
|
||||
# Only suggest if last command failed
|
||||
[[ $exit_code -eq 0 ]] && return
|
||||
|
||||
# Get last command
|
||||
local last_cmd=$(fc -ln -1 2>/dev/null | sed 's/^[[:space:]]*//')
|
||||
[[ -z "$last_cmd" ]] && return
|
||||
|
||||
local first_word="${last_cmd%% *}"
|
||||
|
||||
# Check if it's a git command with typo
|
||||
if [[ "$first_word" == "git" && $exit_code -ne 0 ]]; then
|
||||
local git_subcmd=$(echo "$last_cmd" | awk '{print $2}')
|
||||
local correction="${TYPO_CORRECTIONS[$git_subcmd]}"
|
||||
|
||||
if [[ -n "$correction" ]]; then
|
||||
echo -e "${SS_CYAN}→${SS_NC} Did you mean: ${SS_GREEN}git $correction${SS_NC}?"
|
||||
echo -e "${DF_CYAN}→${DF_NC} Did you mean: ${DF_GREEN}git $correction${DF_NC}?"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -395,18 +262,15 @@ _ss_precmd_hook() {
|
||||
# Quick Fix Function
|
||||
# ============================================================================
|
||||
|
||||
# Run the suggested correction
|
||||
# Usage: !! <correction> or just press up and edit
|
||||
fuck() {
|
||||
local last_cmd=$(fc -ln -1 2>/dev/null | sed 's/^[[:space:]]*//')
|
||||
local first_word="${last_cmd%% *}"
|
||||
|
||||
# Check for typo correction
|
||||
local correction="${TYPO_CORRECTIONS[$first_word]}"
|
||||
|
||||
if [[ -n "$correction" ]]; then
|
||||
local fixed_cmd="${last_cmd/$first_word/$correction}"
|
||||
echo -e "${SS_GREEN}Running:${SS_NC} $fixed_cmd"
|
||||
echo -e "${DF_GREEN}Running:${DF_NC} $fixed_cmd"
|
||||
eval "$fixed_cmd"
|
||||
else
|
||||
echo "No automatic fix available"
|
||||
@@ -419,28 +283,9 @@ fuck() {
|
||||
# ============================================================================
|
||||
|
||||
_ss_setup() {
|
||||
# Add preexec hook
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook preexec _ss_preexec_hook
|
||||
add-zsh-hook precmd _ss_precmd_hook
|
||||
}
|
||||
|
||||
# Initialize
|
||||
[[ "$SMART_SUGGEST_ENABLED" == true ]] && _ss_setup
|
||||
|
||||
# ============================================================================
|
||||
# Usage Examples (commented)
|
||||
# ============================================================================
|
||||
|
||||
# $ gti status
|
||||
# ✗ Command not found: gti
|
||||
# → Did you mean: git?
|
||||
# Run: git status
|
||||
|
||||
# $ dokcer ps
|
||||
# ✗ Command not found: dokcer
|
||||
# → Did you mean: docker?
|
||||
|
||||
# After typing "docker-compose up -d" 10 times:
|
||||
# 💡 Tip: You've typed 'docker-compose up -d' 10 times
|
||||
# Consider adding: alias dcu='docker-compose up -d'
|
||||
|
||||
Reference in New Issue
Block a user