# Profiling: ZSH_BENCH=1 zsh [[ -n "$ZSH_BENCH" ]] && zmodload zsh/zprof # Assume truecolor support if terminal advertises 256color (covers SSH, tmux) [[ -z "$COLORTERM" && "$TERM" == *256color* ]] && export COLORTERM=truecolor _dots_cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/dots" _dots_load_profile() { source "$HOME/.profile" } _dots_load_profile _dots_setup_dirs() { mkdir -p "$XDG_DATA_HOME" "$XDG_CONFIG_HOME" "$HOME/.local/bin" "$WORKSPACE" "$_dots_cache_dir" } _dots_setup_dirs _dots_cache_ls_colors() { local cache_file="$_dots_cache_dir/ls-colours" if [[ -f "$cache_file" ]]; then source "$cache_file" else if ls --color -d . &>/dev/null; then echo 'alias ls="ls --color=auto"' > "$cache_file" elif ls -G -d . &>/dev/null; then echo 'alias ls="ls -G"' > "$cache_file" fi [[ -f "$cache_file" ]] && source "$cache_file" fi } _dots_cache_ls_colors [[ -f ~/.aliases ]] && source ~/.aliases _dots_init_completion() { local comp_dir="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/completions" [[ -d "$comp_dir" ]] && fpath=("$comp_dir" $fpath) autoload -Uz compinit # Daily cache invalidation local dump="$HOME/.zcompdump" if [[ -f "$dump" ]]; then zmodload -F zsh/stat b:zstat 2>/dev/null local -a dump_stat zstat -A dump_stat +mtime "$dump" 2>/dev/null if (( dump_stat[1] > EPOCHSECONDS - 86400 )); then compinit -C else compinit fi else compinit fi } _dots_init_completion _dots_load_plugins() { local plugin_dir="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins" local f="$plugin_dir/zsh-autosuggestions/zsh-autosuggestions.zsh" [[ -f "$f" ]] && source "$f" # syntax-highlighting must be sourced last f="$plugin_dir/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" [[ -f "$f" ]] && source "$f" } _dots_load_plugins _dots_load_history() { HISTFILE="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/history" HISTSIZE=50000 SAVEHIST=50000 [[ -d "${HISTFILE:h}" ]] || mkdir -p "${HISTFILE:h}" setopt HIST_IGNORE_DUPS HIST_IGNORE_SPACE SHARE_HISTORY } _dots_load_history _dots_load_keybindings() { bindkey -e # Ctrl+J: zoxide jump _dots_zoxide_widget() { local result result="$(zoxide query -i -- 2>&1)" && cd "$result" zle reset-prompt } zle -N _dots_zoxide_widget bindkey '^J' _dots_zoxide_widget # Ctrl+B: git branch checkout _dots_git_branch_widget() { local branch branch="$(git branch --all --sort=-committerdate --format='%(refname:short)' 2>/dev/null \ | fzf --preview 'git log --oneline --color -20 {}')" || { zle reset-prompt; return; } branch="${branch#origin/}" git checkout "$branch" 2>&1 zle reset-prompt } zle -N _dots_git_branch_widget bindkey '^B' _dots_git_branch_widget # Ctrl+E: edit file _dots_edit_widget() { local file file="$(fzf --preview 'head -100 {}')" || { zle reset-prompt; return; } ${EDITOR:-vim} "$file" /dev/null awk '{print $1}' ~/.ssh/known_hosts 2>/dev/null | tr ',' '\n' | sed 's/\[//;s/\]:.*//' } | awk '!seen[$0]++' } _dots_ssh_widget() { local host host="$(_dots_ssh_hosts | fzf)" || { zle reset-prompt; return; } BUFFER="ssh $host" zle accept-line } zle -N _dots_ssh_widget bindkey '^G' _dots_ssh_widget # Ctrl+F: find in files _dots_find_in_files_widget() { local selection selection="$(rg --color=always --line-number --no-heading '' 2>/dev/null \ | fzf --ansi --delimiter=: \ --preview 'head -n $((({2}+30))) {1} | tail -n 60' \ --preview-window='right:60%')" || { zle reset-prompt; return; } local file="${selection%%:*}" local line="${${selection#*:}%%:*}" ${EDITOR:-vim} "+$line" "$file" /dev/null ;; pr) gh pr view --web 2>/dev/null ;; issues) gh browse --issues 2>/dev/null ;; actions) gh browse --actions 2>/dev/null ;; esac zle reset-prompt } zle -N _dots_open_widget bindkey '^O' _dots_open_widget # Ctrl+P: project switch _dots_project_widget() { local result result="$(zoxide query -l 2>/dev/null | grep "${WORKSPACE:-$HOME/Workspace}" \ | fzf --preview 'ls -1 {}')" && cd "$result" zle reset-prompt } zle -N _dots_project_widget bindkey '^P' _dots_project_widget } _dots_load_keybindings _dots_load_fzf() { command -v fzf &>/dev/null || return export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git"' export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND" export FZF_DEFAULT_OPTS='--layout=reverse --height=40% --prompt="> " --info=inline-right --no-separator' # fzf --zsh requires v0.48+ if fzf --zsh &>/dev/null; then source <(fzf --zsh) else local -a fzf_paths=( "${HOMEBREW_PREFIX:-/opt/homebrew}/opt/fzf/shell" "/usr/share/fzf" "${XDG_DATA_HOME:-$HOME/.local/share}/fzf/shell" ) for dir in "${fzf_paths[@]}"; do [[ -f "$dir/key-bindings.zsh" ]] && source "$dir/key-bindings.zsh" && break done for dir in "${fzf_paths[@]}"; do [[ -f "$dir/completion.zsh" ]] && source "$dir/completion.zsh" && break done fi } _dots_load_zoxide() { command -v zoxide &>/dev/null || return export _ZO_FZF_OPTS="$FZF_DEFAULT_OPTS" eval "$(zoxide init zsh)" } # Prompt (( ${+PROMPT_MIN_DURATION} )) || typeset -gi PROMPT_MIN_DURATION=2 # show duration after N seconds (( ${+PROMPT_FLASH_DELAY} )) || typeset -gi PROMPT_FLASH_DELAY=4 # flash prompt for N centiseconds typeset -gi _dots_prompt_cmd_start=0 typeset -gi _dots_prompt_cmd_ran=0 typeset -gi _dots_prompt_flashing=0 typeset -g _dots_prompt_base="" typeset -gA _dots_pc _dots_init_colors() { if [[ "$COLORTERM" == (truecolor|24bit) ]]; then _dots_pc=( teal $'%{\e[38;2;44;180;148m%}' orange $'%{\e[38;2;248;140;20m%}' red $'%{\e[38;2;244;4;4m%}' grey $'%{\e[38;2;114;144;184m%}' ) elif [[ "$TERM" == *256color* ]]; then _dots_pc=( teal $'%{\e[38;5;43m%}' orange $'%{\e[38;5;208m%}' red $'%{\e[38;5;196m%}' grey $'%{\e[38;5;103m%}' ) else _dots_pc=( teal $'%{\e[36m%}' orange $'%{\e[33m%}' red $'%{\e[31m%}' grey $'%{\e[34m%}' ) fi _dots_pc[reset]=$'%{\e[0m%}' _dots_pc[bold]=$'%{\e[1m%}' } _dots_abbrev_path() { local dir="${PWD/#$HOME/~}" local -a parts=( "${(@s:/:)dir}" ) local count=${#parts[@]} (( count <= 3 )) && { print -r -- "$dir"; return } local result="" local i for (( i=1; i <= count-3; i++ )); do result+="${parts[i][1]}/" done print -r -- "${result}${parts[-3]}/${parts[-2]}/${parts[-1]}" } _dots_session() { [[ -n "$CODESPACE_NAME" ]] && { print -r -- "$CODESPACE_NAME"; return } [[ -n "$SSH_CONNECTION" || -n "$SSH_CLIENT" || -n "$SSH_TTY" ]] && { print -r -- "%n@%m"; return } [[ -f /.dockerenv ]] && { print -r -- "${DEVCONTAINER_ID:-$(/dev/null) || return local branch="" ahead=0 behind=0 staged=0 unstaged=0 untracked=0 # Parse efficiently: branch info from first line, counts from rest local first_line="${output%%$'\n'*}" # Extract branch from "## branch...remote [ahead N, behind M]" if [[ "$first_line" == "## "* ]]; then branch="${first_line#\#\# }" # Handle detached HEAD if [[ "$branch" == "HEAD (no branch)" || "$branch" == [0-9a-f]##* ]]; then branch="${branch:0:7}" else # Remove tracking info branch="${branch%%...*}" branch="${branch%% *}" fi # Extract ahead/behind [[ "$first_line" =~ "ahead ([0-9]+)" ]] && ahead="${match[1]}" [[ "$first_line" =~ "behind ([0-9]+)" ]] && behind="${match[1]}" fi [[ -z "$branch" ]] && return # Count file status with awk (fast, no shell loops) local -a counts counts=( $(awk ' NR == 1 { next } # Skip branch line /^\?\?/ { untracked++; next } { x = substr($0, 1, 1) y = substr($0, 2, 1) if (x != " " && x != "?") staged++ if (y != " " && y != "?") unstaged++ } END { print staged+0, unstaged+0, untracked+0 } ' <<< "$output") ) staged=${counts[1]:-0} unstaged=${counts[2]:-0} untracked=${counts[3]:-0} local info="${_dots_pc[grey]}(${branch})${_dots_pc[reset]}" local dirty="" local sep="" if (( staged )); then dirty+="${_dots_pc[teal]}+${staged}${_dots_pc[reset]}" sep=" " fi if (( unstaged )); then dirty+="${sep}${_dots_pc[orange]}~${unstaged}${_dots_pc[reset]}" sep=" " fi if (( untracked )); then dirty+="${sep}${_dots_pc[grey]}?${untracked}${_dots_pc[reset]}" fi local arrows="" sep="" if (( ahead )); then arrows+="${_dots_pc[teal]}↑${ahead}${_dots_pc[reset]}" sep=" " fi if (( behind )); then arrows+="${sep}${_dots_pc[orange]}↓${behind}${_dots_pc[reset]}" fi if [[ -n "$dirty" || -n "$arrows" ]]; then info+=" " [[ -n "$dirty" ]] && info+="$dirty" [[ -n "$dirty" && -n "$arrows" ]] && info+=" " [[ -n "$arrows" ]] && info+="$arrows" fi print -r -- "$info" } # Async git info typeset -g _dots_git_info_result="" typeset -g _dots_git_info_pwd="" typeset -g _dots_git_async_fd="" _dots_git_async_callback() { local fd=$1 local result="" # Use sysread for efficient non-blocking read from fd if [[ -n "$fd" ]] && sysread -i "$fd" result 2>/dev/null; then result="${result%$'\n'}" # trim trailing newline _dots_git_info_result="$result" _dots_build_dots_prompt_base PROMPT="$_dots_prompt_base" zle && zle reset-prompt fi # Clean up exec {fd}<&- zle -F "$fd" 2>/dev/null _dots_git_async_fd="" } _dots_git_async_start() { # Check if we're in a git repo local git_dir git_dir=$(git rev-parse --git-dir 2>/dev/null) || return # Cancel any pending async job (reuse single worker) if [[ -n "$_dots_git_async_fd" ]]; then exec {_dots_git_async_fd}<&- 2>/dev/null zle -F "$_dots_git_async_fd" 2>/dev/null _dots_git_async_fd="" fi # Start background job exec {_dots_git_async_fd}< <( _dots_git_info_sync ) zle -F "$_dots_git_async_fd" _dots_git_async_callback } _dots_build_dots_prompt_base() { local dir_path="$(_dots_abbrev_path)" local symbol="${_dots_pc[grey]}>${_dots_pc[reset]}" (( EUID == 0 )) && symbol="${_dots_pc[orange]}${_dots_pc[bold]}#${_dots_pc[reset]}" local line1="${_dots_pc[teal]}${dir_path}${_dots_pc[reset]}" [[ -n "$_dots_git_info_result" ]] && line1+=" ${_dots_git_info_result}" _dots_prompt_base=$'\n'"${line1}"$'\n'"${symbol} " } _dots_preexec() { _dots_prompt_cmd_start=$EPOCHSECONDS _dots_prompt_cmd_ran=1 } _dots_precmd() { local e=$? d=0 # Only show exit code if a command actually ran (( _dots_prompt_cmd_ran )) || e=0 _dots_prompt_cmd_ran=0 # First prompt should never show error from shell init [[ -z "$_dots_first_prompt" ]] && { _dots_first_prompt=1; e=0; } # Build RPROMPT: time, error, host local rp_parts=() if (( _dots_prompt_cmd_start )); then d=$(( EPOCHSECONDS - _dots_prompt_cmd_start )) _dots_prompt_cmd_start=0 if (( d >= PROMPT_MIN_DURATION )); then (( d >= 60 )) && rp_parts+=("${_dots_pc[grey]}($(( d/60 ))m$(( d%60 ))s)${_dots_pc[reset]}") \ || rp_parts+=("${_dots_pc[grey]}(${d}s)${_dots_pc[reset]}") fi fi (( e )) && rp_parts+=("${_dots_pc[red]}[${e}]${_dots_pc[reset]}") local session="$(_dots_session)" [[ -n "$session" ]] && rp_parts+=("${_dots_pc[orange]}[${session}]${_dots_pc[reset]}") RPROMPT="${(j: :)rp_parts}" # On directory change, clear git info until async refreshes if [[ "$PWD" != "$_dots_git_info_pwd" ]]; then _dots_git_info_pwd="$PWD" _dots_git_info_result="" fi _dots_build_dots_prompt_base _dots_git_async_start PROMPT="$_dots_prompt_base" } TRAPINT() { # Only customize when ZLE is active (at prompt, not during command) if [[ -o zle ]] && [[ -o interactive ]] && (( ${+WIDGET} )); then if [[ -z "$BUFFER" ]] && (( ! _dots_prompt_flashing )); then # Empty buffer: flash the prompt symbol _dots_prompt_flashing=1 local git_part="" [[ -n "$_dots_git_info_result" ]] && git_part=" ${_dots_git_info_result}" local flash_prompt=$'\n'"${_dots_pc[teal]}$(_dots_abbrev_path)${_dots_pc[reset]}${git_part}"$'\n'$'%{\e[48;2;248;140;20m\e[30m%}> %{\e[0m%}' PROMPT="$flash_prompt" zle reset-prompt zselect -t $PROMPT_FLASH_DELAY _dots_prompt_flashing=0 PROMPT="$_dots_prompt_base" zle reset-prompt return 0 elif [[ -n "$BUFFER" ]]; then # Buffer has content: clear autosuggest, then default behavior zle autosuggest-clear 2>/dev/null fi fi # Propagate signal: use special return code -1 to let zsh handle normally return $((128 + ${1:-2})) } _dots_prompt_init() { zmodload zsh/datetime 2>/dev/null zmodload zsh/zselect 2>/dev/null zmodload zsh/system 2>/dev/null _dots_init_colors _dots_build_dots_prompt_base setopt PROMPT_SUBST EXTENDED_HISTORY INC_APPEND_HISTORY_TIME autoload -Uz add-zsh-hook add-zsh-hook preexec _dots_preexec add-zsh-hook precmd _dots_precmd add-zsh-hook chpwd _dots_build_dots_prompt_base PROMPT="$_dots_prompt_base" RPROMPT="" } _dots_prompt_init _dots_load_mise() { command -v mise &>/dev/null && eval "$(mise activate zsh)" } _dots_load_mise _dots_load_fzf _dots_load_zoxide [[ -n "$ZSH_BENCH" ]] && zprof || true