Dotfiles update 2025-12-25 15:45
This commit is contained in:
500
zsh/functions/fzf-extras.zsh
Normal file
500
zsh/functions/fzf-extras.zsh
Normal file
@@ -0,0 +1,500 @@
|
||||
# ============================================================================
|
||||
# FZF-Powered Utilities
|
||||
# ============================================================================
|
||||
# Additional fuzzy finders for various system exploration tasks.
|
||||
#
|
||||
# Features:
|
||||
# - envf: Environment variable browser
|
||||
# - pathf: PATH explorer
|
||||
# - procf: Process manager
|
||||
# - aliasf: Alias browser
|
||||
# - funcf: Function browser
|
||||
# - histf: Enhanced history search
|
||||
# ============================================================================
|
||||
|
||||
# Prevent double-sourcing
|
||||
[[ -n "$_DF_FZF_EXTRAS_LOADED" ]] && return 0
|
||||
typeset -g _DF_FZF_EXTRAS_LOADED=1
|
||||
|
||||
# Source utils
|
||||
source "${0:A:h}/../lib/utils.zsh" 2>/dev/null || \
|
||||
source "$HOME/.dotfiles/zsh/lib/utils.zsh" 2>/dev/null
|
||||
|
||||
# ============================================================================
|
||||
# Check FZF
|
||||
# ============================================================================
|
||||
|
||||
_fzf_check() {
|
||||
if ! command -v fzf &>/dev/null; then
|
||||
df_print_error "fzf not installed"
|
||||
df_print_info "Install: sudo pacman -S fzf"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Common fzf options
|
||||
_fzf_common_opts() {
|
||||
echo "--height=60% --layout=reverse --border=rounded --info=inline"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Environment Variable Browser
|
||||
# ============================================================================
|
||||
|
||||
envf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(env | sort | fzf $(_fzf_common_opts) \
|
||||
--prompt="Env > " \
|
||||
--preview='echo {} | cut -d= -f1 | xargs -I{} bash -c "echo -e \"Variable: {}\n\nValue:\n\"; printenv {}"' \
|
||||
--preview-window=right:50%:wrap \
|
||||
--header="Enter: copy value | Ctrl-E: edit | Ctrl-U: unset")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
local var_name="${selected%%=*}"
|
||||
local var_value="${selected#*=}"
|
||||
|
||||
echo ""
|
||||
echo -e "${DF_CYAN}$var_name${DF_NC}=$var_value"
|
||||
echo ""
|
||||
|
||||
# Copy to clipboard if available
|
||||
if command -v wl-copy &>/dev/null; then
|
||||
echo -n "$var_value" | wl-copy
|
||||
df_print_success "Value copied to clipboard"
|
||||
elif command -v xclip &>/dev/null; then
|
||||
echo -n "$var_value" | xclip -selection clipboard
|
||||
df_print_success "Value copied to clipboard"
|
||||
fi
|
||||
}
|
||||
|
||||
# Set/edit environment variable interactively
|
||||
env-set() {
|
||||
local var_name="$1"
|
||||
|
||||
if [[ -z "$var_name" ]]; then
|
||||
_fzf_check || return 1
|
||||
var_name=$(env | cut -d= -f1 | sort | fzf $(_fzf_common_opts) \
|
||||
--prompt="Select var to edit > " \
|
||||
--header="Select existing variable or type new name")
|
||||
[[ -z "$var_name" ]] && return
|
||||
fi
|
||||
|
||||
local current_value="${(P)var_name}"
|
||||
|
||||
echo "Variable: $var_name"
|
||||
echo "Current: ${current_value:-(not set)}"
|
||||
echo ""
|
||||
read -r "new_value?New value: "
|
||||
|
||||
if [[ -n "$new_value" ]]; then
|
||||
export "$var_name"="$new_value"
|
||||
df_print_success "Set $var_name=$new_value"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PATH Explorer
|
||||
# ============================================================================
|
||||
|
||||
pathf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(echo "$PATH" | tr ':' '\n' | nl -ba | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="PATH > " \
|
||||
--preview='dir=$(echo {} | awk "{print \$2}");
|
||||
if [[ -d "$dir" ]]; then
|
||||
echo "Directory: $dir"
|
||||
echo ""
|
||||
echo "Executables:"
|
||||
ls -1 "$dir" 2>/dev/null | head -30
|
||||
count=$(ls -1 "$dir" 2>/dev/null | wc -l)
|
||||
[[ $count -gt 30 ]] && echo "... and $((count-30)) more"
|
||||
else
|
||||
echo "Directory not found: $dir"
|
||||
fi' \
|
||||
--preview-window=right:50% \
|
||||
--header="PATH entries (in order)")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
local dir=$(echo "$selected" | awk '{print $2}')
|
||||
|
||||
echo ""
|
||||
df_print_section "Directory: $dir"
|
||||
|
||||
if [[ -d "$dir" ]]; then
|
||||
ls -la "$dir" | head -20
|
||||
else
|
||||
df_print_warning "Directory does not exist"
|
||||
fi
|
||||
}
|
||||
|
||||
# Add to PATH interactively
|
||||
path-add() {
|
||||
local dir="${1:-$PWD}"
|
||||
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
df_print_error "Not a directory: $dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
dir=$(realpath "$dir")
|
||||
|
||||
if [[ ":$PATH:" == *":$dir:"* ]]; then
|
||||
df_print_warning "Already in PATH: $dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Add to PATH: $dir"
|
||||
echo ""
|
||||
echo "1) Prepend (higher priority)"
|
||||
echo "2) Append (lower priority)"
|
||||
echo "3) Cancel"
|
||||
echo ""
|
||||
|
||||
read -k1 "choice?Choice [1]: "
|
||||
echo ""
|
||||
|
||||
case "${choice:-1}" in
|
||||
1)
|
||||
export PATH="$dir:$PATH"
|
||||
df_print_success "Prepended to PATH"
|
||||
;;
|
||||
2)
|
||||
export PATH="$PATH:$dir"
|
||||
df_print_success "Appended to PATH"
|
||||
;;
|
||||
*)
|
||||
echo "Cancelled"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Process Manager
|
||||
# ============================================================================
|
||||
|
||||
procf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(ps aux --sort=-%mem | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="Process > " \
|
||||
--header-lines=1 \
|
||||
--preview='pid=$(echo {} | awk "{print \$2}");
|
||||
echo "=== Process Details ==="
|
||||
ps -p $pid -o pid,ppid,user,%cpu,%mem,stat,start,time,cmd 2>/dev/null
|
||||
echo ""
|
||||
echo "=== Open Files (first 10) ==="
|
||||
sudo lsof -p $pid 2>/dev/null | head -10 || echo "(requires sudo)"
|
||||
echo ""
|
||||
echo "=== Environment (first 10) ==="
|
||||
sudo cat /proc/$pid/environ 2>/dev/null | tr "\0" "\n" | head -10 || echo "(requires sudo)"' \
|
||||
--preview-window=right:50%:wrap \
|
||||
--header="Process list | Enter: details | Ctrl-K: kill")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
local pid=$(echo "$selected" | awk '{print $2}')
|
||||
local cmd=$(echo "$selected" | awk '{for(i=11;i<=NF;i++) printf $i" "; print ""}')
|
||||
|
||||
echo ""
|
||||
df_print_section "Selected Process"
|
||||
echo " PID: $pid"
|
||||
echo " CMD: $cmd"
|
||||
echo ""
|
||||
echo "Actions:"
|
||||
echo " 1) Show details"
|
||||
echo " 2) Send SIGTERM (graceful)"
|
||||
echo " 3) Send SIGKILL (force)"
|
||||
echo " 4) Send SIGHUP (reload)"
|
||||
echo " 5) Cancel"
|
||||
echo ""
|
||||
|
||||
read -k1 "action?Action [1]: "
|
||||
echo ""
|
||||
|
||||
case "${action:-1}" in
|
||||
1)
|
||||
ps -p "$pid" -f
|
||||
;;
|
||||
2)
|
||||
df_print_step "Sending SIGTERM to $pid..."
|
||||
kill -TERM "$pid" 2>/dev/null && df_print_success "Signal sent" || df_print_error "Failed (try sudo)"
|
||||
;;
|
||||
3)
|
||||
df_print_step "Sending SIGKILL to $pid..."
|
||||
kill -KILL "$pid" 2>/dev/null && df_print_success "Signal sent" || df_print_error "Failed (try sudo)"
|
||||
;;
|
||||
4)
|
||||
df_print_step "Sending SIGHUP to $pid..."
|
||||
kill -HUP "$pid" 2>/dev/null && df_print_success "Signal sent" || df_print_error "Failed (try sudo)"
|
||||
;;
|
||||
*)
|
||||
echo "Cancelled"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Quick kill by name
|
||||
killf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(ps aux | grep -v "grep" | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="Kill > " \
|
||||
--header-lines=1 \
|
||||
--multi \
|
||||
--header="Select process(es) to kill (Tab to select multiple)")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
echo "$selected" | while read -r line; do
|
||||
local pid=$(echo "$line" | awk '{print $2}')
|
||||
local cmd=$(echo "$line" | awk '{for(i=11;i<=NF;i++) printf $i" "; print ""}')
|
||||
|
||||
df_print_step "Killing PID $pid ($cmd)"
|
||||
kill "$pid" 2>/dev/null && df_print_success "Killed" || df_print_error "Failed"
|
||||
done
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Alias Browser
|
||||
# ============================================================================
|
||||
|
||||
aliasf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(alias | sed "s/^alias //" | sort | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="Alias > " \
|
||||
--preview='name=$(echo {} | cut -d= -f1);
|
||||
cmd=$(echo {} | cut -d= -f2- | sed "s/^'\''//;s/'\''$//");
|
||||
echo "Alias: $name"
|
||||
echo ""
|
||||
echo "Expands to:"
|
||||
echo "$cmd"
|
||||
echo ""
|
||||
echo "Type: $(type $name 2>/dev/null || echo "alias")"' \
|
||||
--preview-window=right:50%:wrap \
|
||||
--header="Enter: insert alias | Ctrl-E: edit definition")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
local alias_name="${selected%%=*}"
|
||||
print -z "$alias_name "
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Function Browser
|
||||
# ============================================================================
|
||||
|
||||
funcf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(print -l ${(ok)functions} | grep -v "^_" | sort | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="Function > " \
|
||||
--preview='whence -f {}' \
|
||||
--preview-window=right:60%:wrap \
|
||||
--header="Shell functions | Enter: insert | Ctrl-V: view source")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
print -z "$selected "
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Enhanced History Search
|
||||
# ============================================================================
|
||||
|
||||
histf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local selected=$(fc -ln 1 | tac | awk '!seen[$0]++' | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="History > " \
|
||||
--no-sort \
|
||||
--header="Command history (newest first) | Enter: execute | Ctrl-E: edit")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
print -z "$selected"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# File Finder (enhanced)
|
||||
# ============================================================================
|
||||
|
||||
ff() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local search_dir="${1:-.}"
|
||||
local query="${2:-}"
|
||||
|
||||
local cmd="find $search_dir -type f 2>/dev/null"
|
||||
|
||||
# Use fd if available (faster)
|
||||
if command -v fd &>/dev/null; then
|
||||
cmd="fd --type f . $search_dir"
|
||||
fi
|
||||
|
||||
local selected=$(eval "$cmd" | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--query="$query" \
|
||||
--prompt="File > " \
|
||||
--preview='
|
||||
file={}
|
||||
if file "$file" | grep -q "text"; then
|
||||
bat --style=numbers --color=always "$file" 2>/dev/null || cat "$file"
|
||||
else
|
||||
file "$file"
|
||||
echo ""
|
||||
ls -lh "$file"
|
||||
fi' \
|
||||
--preview-window=right:60% \
|
||||
--header="Files | Enter: open | Ctrl-E: edit | Ctrl-Y: copy path")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
echo "$selected"
|
||||
}
|
||||
|
||||
# Open file with appropriate application
|
||||
ffo() {
|
||||
local file=$(ff "$@")
|
||||
[[ -z "$file" ]] && return
|
||||
|
||||
if [[ -f "$file" ]]; then
|
||||
if file "$file" | grep -q "text"; then
|
||||
${EDITOR:-vim} "$file"
|
||||
else
|
||||
xdg-open "$file" 2>/dev/null || open "$file" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Directory Finder
|
||||
# ============================================================================
|
||||
|
||||
fdir() {
|
||||
_fzf_check || return 1
|
||||
|
||||
local search_dir="${1:-.}"
|
||||
|
||||
local cmd="find $search_dir -type d 2>/dev/null"
|
||||
|
||||
if command -v fd &>/dev/null; then
|
||||
cmd="fd --type d . $search_dir"
|
||||
fi
|
||||
|
||||
local selected=$(eval "$cmd" | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--prompt="Directory > " \
|
||||
--preview='ls -la {} | head -30' \
|
||||
--preview-window=right:50% \
|
||||
--header="Directories | Enter: cd")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
cd "$selected"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Git Helpers
|
||||
# ============================================================================
|
||||
|
||||
# Git branch switcher
|
||||
gbf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
if ! git rev-parse --git-dir &>/dev/null; then
|
||||
df_print_error "Not a git repository"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local selected=$(git branch -a --color=always | grep -v '/HEAD\s' | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--ansi \
|
||||
--prompt="Branch > " \
|
||||
--preview='git log --oneline --graph --color=always $(echo {} | sed "s/.* //" | sed "s#remotes/##") -- | head -20' \
|
||||
--header="Git branches | Enter: checkout")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
local branch=$(echo "$selected" | sed "s/.* //" | sed "s#remotes/[^/]*/##")
|
||||
git checkout "$branch"
|
||||
}
|
||||
|
||||
# Git commit browser
|
||||
glogf() {
|
||||
_fzf_check || return 1
|
||||
|
||||
if ! git rev-parse --git-dir &>/dev/null; then
|
||||
df_print_error "Not a git repository"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local selected=$(git log --oneline --color=always | \
|
||||
fzf $(_fzf_common_opts) \
|
||||
--ansi \
|
||||
--prompt="Commit > " \
|
||||
--preview='git show --color=always $(echo {} | cut -d" " -f1)' \
|
||||
--preview-window=right:60% \
|
||||
--header="Git commits | Enter: show | Ctrl-D: diff")
|
||||
|
||||
[[ -z "$selected" ]] && return
|
||||
|
||||
local commit=$(echo "$selected" | cut -d" " -f1)
|
||||
git show "$commit"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Help
|
||||
# ============================================================================
|
||||
|
||||
fzf-help() {
|
||||
df_print_func_name "FZF Utilities"
|
||||
cat << 'EOF'
|
||||
|
||||
Environment:
|
||||
envf Browse environment variables
|
||||
env-set [VAR] Set/edit environment variable
|
||||
|
||||
Path:
|
||||
pathf Explore PATH directories
|
||||
path-add [DIR] Add directory to PATH
|
||||
|
||||
Process:
|
||||
procf Browse and manage processes
|
||||
killf Fuzzy kill processes
|
||||
|
||||
Shell:
|
||||
aliasf Browse aliases
|
||||
funcf Browse functions
|
||||
histf Search command history
|
||||
|
||||
Files:
|
||||
ff [DIR] Find files
|
||||
ffo [DIR] Find and open file
|
||||
fdir [DIR] Find and cd to directory
|
||||
|
||||
Git:
|
||||
gbf Branch switcher
|
||||
glogf Commit browser
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Aliases
|
||||
# ============================================================================
|
||||
|
||||
alias envbrowse='envf'
|
||||
alias pathbrowse='pathf'
|
||||
alias proc='procf'
|
||||
Reference in New Issue
Block a user