aboutsummaryrefslogtreecommitdiff
path: root/linux/home/.config/zsh/user
diff options
context:
space:
mode:
authorsrdusr <trevorgray@srdusr.com>2025-09-01 15:40:50 +0200
committersrdusr <trevorgray@srdusr.com>2025-09-01 15:40:50 +0200
commit40fc89461967f0094911e8846117ed3190e42d0b (patch)
treeed22b71d8a3036fb7e0e2a48ec6a09f99f9da365 /linux/home/.config/zsh/user
parentecd73a73200a64b5c62614debd9f9ec08748a72d (diff)
downloaddotfiles-40fc89461967f0094911e8846117ed3190e42d0b.tar.gz
dotfiles-40fc89461967f0094911e8846117ed3190e42d0b.zip
Testing
Diffstat (limited to 'linux/home/.config/zsh/user')
-rw-r--r--linux/home/.config/zsh/user/aliases.zsh210
-rw-r--r--linux/home/.config/zsh/user/bindings.zsh121
-rw-r--r--linux/home/.config/zsh/user/completion.zsh24
-rw-r--r--linux/home/.config/zsh/user/functions.zsh884
-rw-r--r--linux/home/.config/zsh/user/options.zsh69
-rw-r--r--linux/home/.config/zsh/user/prompt.zsh568
-rw-r--r--linux/home/.config/zsh/user/prompt_minimal.zsh295
-rw-r--r--linux/home/.config/zsh/user/prompt_new.zsh863
-rw-r--r--linux/home/.config/zsh/user/prompt_simple.zsh227
9 files changed, 3045 insertions, 216 deletions
diff --git a/linux/home/.config/zsh/user/aliases.zsh b/linux/home/.config/zsh/user/aliases.zsh
index df0c4af..3bd661a 100644
--- a/linux/home/.config/zsh/user/aliases.zsh
+++ b/linux/home/.config/zsh/user/aliases.zsh
@@ -1,10 +1,130 @@
########## Aliases ##########
### Dotfiles
-alias config='git --git-dir=$HOME/.cfg --work-tree=$HOME'
-cfg_files=$(config ls-tree --name-only -r HEAD)
+#if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
+# # normal bare repo alias
+# #alias _config='git --git-dir=$HOME/.cfg --work-tree=$HOME'
+#
+# _config() {
+# git --git-dir="$HOME/.cfg" --work-tree="$HOME" "$@"
+# }
+#
+# config() {
+# if [ "$1" = "add" ]; then
+# shift
+# for f in "$@"; do
+# case "$(uname -s)" in
+# Linux)
+# _config add -- "linux/home/$f"
+# ;;
+# Darwin)
+# _config add -- "macos/home/$f"
+# ;;
+# MINGW*|MSYS*|CYGWIN*)
+# _config add -- "windows/Documents/$f"
+# ;;
+# *)
+# _config add -- "$f"
+# ;;
+# esac
+# done
+# else
+# _config "$@"
+# fi
+# }
+#
+# cfg_files=$(_config ls-tree --name-only -r HEAD 2>/dev/null)
+# export CFG_FILES="$cfg_files"
+#fi
-export CFG_FILES="$cfg_files"
+## Only run if bare repo exists
+#if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
+#
+# # raw bare-repo command
+# _config() {
+# git --git-dir="$HOME/.cfg" --work-tree="$HOME" "$@"
+# }
+#
+# # helper to map paths to OS-specific directories
+#
+# _os_path() {
+# local f="$1"
+# # if user already gave a path with prefix, just return it unchanged
+# case "$f" in
+# linux/*|macos/*|windows/*|common/*|profile/*)
+# echo "$f"
+# return
+# ;;
+# esac
+#
+# # otherwise map according to OS
+# case "$(uname -s)" in
+# Linux) echo "linux/home/$f" ;;
+# Darwin) echo "macos/home/$f" ;;
+# MINGW*|MSYS*|CYGWIN*) echo "windows/Documents/$f" ;;
+# *) echo "$f" ;;
+# esac
+# }
+#
+# # wrapper
+# config() {
+# if [ "$1" = "add" ]; then
+# shift
+# for f in "$@"; do
+# case "$(uname -s)" in
+# Linux)
+# case "$f" in
+# /*) # absolute path
+# rel="${f#$HOME/}" # strip leading /home/username/
+# if [[ "$rel" = "$f" ]]; then
+# # wasn't under $HOME, just strip /
+# rel="${f#/}"
+# _config add -- "linux/$rel"
+# else
+# _config add -- "linux/home/$rel"
+# fi
+# ;;
+# *)
+# _config add -- "linux/home/$f"
+# ;;
+# esac
+# ;;
+# Darwin)
+# case "$f" in
+# /*)
+# rel="${f#/}"
+# _config add -- "macos/$rel"
+# ;;
+# *)
+# _config add -- "macos/home/$f"
+# ;;
+# esac
+# ;;
+# MINGW*|MSYS*|CYGWIN*)
+# case "$f" in
+# /*)
+# rel="${f#/}"
+# _config add -- "windows/$rel"
+# ;;
+# *)
+# _config add -- "windows/Documents/$f"
+# ;;
+# esac
+# ;;
+# *)
+# _config add -- "$f"
+# ;;
+# esac
+# done
+# else
+# _config "$@"
+# fi
+# }
+#
+# # export tracked files if needed
+# cfg_files=$(_config ls-tree --name-only -r HEAD 2>/dev/null)
+# export CFG_FILES="$cfg_files"
+#fi
# Define alias for nvim/vim (fallback to vim)
if command -v nvim > /dev/null; then
@@ -13,16 +133,23 @@ else
alias vi='vim'
fi
+#alias vv='$(history -p !vim)'
+alias vv="vim -c 'norm! ^O'"
+
# Confirmation #
alias mv='mv -i'
alias cp='cp -i'
alias ln='ln -i'
# Disable 'rm'
-alias rm='function _rm() { echo -e "\033[0;31mrm\033[0m is disabled, use \033[0;32mtrash\033[0m or \033[0;32mdel \033[0m\033[0;33m$1\033[0m"; }; _rm'
-alias del='/bin/rm'
+#alias rm='function _rm() { echo -e "\033[0;31mrm\033[0m is disabled, use \033[0;32mtrash\033[0m or \033[0;32mdel \033[0m\033[0;33m$1\033[0m"; }; _rm'
+#alias del='/bin/rm'
-alias ls='lsd --color=auto --group-directories-first'
+# Use lsd for ls if available
+if command -v lsd >/dev/null 2>&1; then
+ alias ls='lsd --color=auto --group-directories-first'
+fi
+#alias ls='lsd --all --color=auto --group-directories-first'
#alias ls="ls --color=auto --group-directories-first"
# ls variants
@@ -43,7 +170,7 @@ alias lrt='ls -1Fcrt --group-directories-first'
# suffix aliases
alias -g CP='| xclip -selection clipboard -rmlastnl'
-alias -g LL="| less"
+alias -g LL="| less exit 2>1 /dev/null"
alias -g CA="| cat -A"
alias -g KE="2>&1"
alias -g NE="2>/dev/null"
@@ -53,9 +180,16 @@ alias grep='grep --color=auto --exclude-dir={.git,.svn,.hg}'
alias egrep='egrep --color=auto --exclude-dir={.git,.svn,.hg}'
alias egrep='fgrep --color=auto --exclude-dir={.git,.svn,.hg}'
+#alias hist="grep '$1' $HISTFILE"
+alias hist="history | grep $1"
+
+
alias gdb='gdb -q'
alias rust-gdb='rust-gdb -q'
+alias cd="cd-clear-ls"
+alias clear='newline_clear'
+
# List upto last 10 visited directories using "d" and quickly cd into any specific one
alias d="dirs -v | head -10"
@@ -75,7 +209,9 @@ alias sudo='sudo ' # zsh: elligible for alias expansion/fix syntax highlight
alias sedit='sudoedit'
#alias se='sudoedit'
alias se='sudo -e'
-alias :q='exit'
+alias :q='exit 2>1 /dev/null'
+alias disk-destroyer='$(command -v dd)'
+alias dd='echo "Warning use command: disk-destroyer"'
alias sc="systemctl"
alias jc="journalctl"
alias jck="journalctl -k" # Kernel
@@ -84,31 +220,55 @@ alias journalctl-error='sudo journalctl -b --priority 0..3'
alias jcssh="sudo journalctl -u sshd"
alias tunnel='ssh -fNTL'
# tty aliases
-if [[ "$TERM" == 'linux' ]]; then
- alias tmux='/usr/bin/tmux -L linux'
-fi
-alias logout="loginctl kill-user $(whoami)"
+#if [[ "$TERM" == 'linux' ]]; then
+# alias tmux='/usr/bin/tmux -L linux'
+#fi
+#alias logout="loginctl kill-user $(whoami)"
+
+logout() {
+ local wm
+ wm="$(windowManagerName)"
+ if [[ -n "$wm" ]]; then
+ echo "Logging out by killing window manager: $wm"
+ pkill "$wm"
+ else
+ echo "No window manager detected!" >&2
+ fi
+}
+alias lg="logout"
#alias suspend='systemctl suspend && betterlockscreen -l' # Suspend(sleep) and lock screen if using systemctl
-alias suspend='systemctl suspend' # Suspend(sleep) and lock screen if using systemctl
-alias hibernate='systemctl hibernate' # Hibernate
+#alias suspend='systemctl suspend' # Suspend(sleep) and lock screen if using systemctl
+alias suspend='loginctl suspend' # Suspend(sleep) and lock screen if using systemctl
+#alias shutdown='loginctl poweroff' # Suspend(sleep) and lock screen if using systemctl
+#alias shutdown='sudo /sbin/shutdown -h'
+#alias poweroff='loginctl poweroff'
+#alias reboot='loginctl reboot'
+alias reboot='sudo reboot'
+#alias hibernate='systemctl hibernate' # Hibernate
alias lock='DISPLAY=:0 xautolock -locknow' # Lock my workstation screen from my phone
alias oports="sudo lsof -i -P -n | grep -i 'listen'" # List open ports
alias keyname="xev | sed -n 's/[ ]*state.* \([^ ]*\)).*/\1/p'"
alias wget=wget --hsts-file="$XDG_CACHE_HOME/wget-hsts" # wget does not support environment variables
+alias open="xdg-open"
alias pp='getlast 2>&1 |&tee -a output.txt'
-alias lg='la | grep'
+#alias lg='la | grep'
alias pg='ps aux | grep'
alias py='python'
alias py3='python3'
+alias activate='source ~/.local/share/venv/bin/activate'
alias sha256='shasum -a 256'
alias rgf='rg -F'
alias weather='curl wttr.in/durban'
+alias diary='nvim "$HOME/documents/main/inbox/diary/$(date +'%Y-%m-%d').md"'
alias wifi='nmcli dev wifi show-password'
alias ddg='w3m lite.duckduckgo.com'
alias rss='newsboat'
alias vpn='protonvpn'
alias yt-dl="yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4' --restrict-filename"
+#alias com.obsproject.Studio="obs"
+#alias obs="com.obsproject.Studio"
+#alias obs-stuido="obs"
# Time aliases
alias utc='TZ=Africa/Johannesburg date'
@@ -117,13 +277,27 @@ alias nyc='TZ=America/New_York date'
alias sfo='TZ=America/Los_Angeles date'
alias utc='TZ=Etc/UTC date'
-alias src='source ~/.zshrc'
+alias src='source $ZDOTDIR/.zshrc'
alias p=proxy
alias cheat='~/.scripts/cheat.sh ~/documents/notes/cheatsheets'
alias crypto='curl -s rate.sx | head -n -2 | tail -n +10'
-alias todo='glow "$HOME"/media/notes/_TODO.md'
+#alias todo='glow "$HOME"/documents/main/notes/TODO.md'
+alias todo='$EDITOR "$(find "$HOME"/documents/main -type f -iname "todo.md" | head -n 1)"'
alias android-studio='/opt/android-studio/bin/studio.sh' # android-studio
alias nomachine='/usr/NX/bin/nxplayer' # nomachine
-alias spotify='LD_PRELOAD=/usr/lib/spotify-adblock.so /bin/spotify %U'
+alias firefox="firefox-bin"
+alias discord="vesktop-bin"
+alias fetch="fastfetch"
+#alias land="env GDK_BACKEND=wayland $HOME/.local/bin/land"
+#alias land="env env WAYLAND_DISPLAY=wayland-1 $HOME/.local/bin/land"
+alias bat='upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -E "state|to full|percentage"'
+alias emerge-fetch='sudo tail -f /var/log/emerge-fetch.log'
+#alias spotify="env LD_PRELOAD=/usr/lib64/spotify-adblock.so spotify %U"
+#Exec=env LD_PRELOAD=/usr/lib64/spotify-adblock.so spotify %U
+alias spotify="env LD_PRELOAD=/usr/local/lib/spotify-adblock.so spotify %U"
+#alias spotify='LD_PRELOAD=/usr/lib/spotify-adblock.so /bin/spotify %U'
+
+alias proofread='firejail --private --private-tmp --net=none --seccomp --caps.drop=all zathura'
+
diff --git a/linux/home/.config/zsh/user/bindings.zsh b/linux/home/.config/zsh/user/bindings.zsh
index ce8451b..ecfb7a8 100644
--- a/linux/home/.config/zsh/user/bindings.zsh
+++ b/linux/home/.config/zsh/user/bindings.zsh
@@ -21,12 +21,25 @@ bindkey "^U" backward-kill-line
bindkey "^[j" history-search-forward # or you can bind it to the down key "^[[B"
bindkey "^[k" history-search-backward # or you can bind it to Up key "^[[A"
+bindkey '^[[D' backward-char # Left arrow
+bindkey '^[[C' forward-char # Right arrow
+bindkey '^[D' backward-char # Left arrow
+bindkey '^[C' forward-char # Right arrow
+bindkey '[C' forward-word
+bindkey '[D' backward-word
+bindkey -M viins '^[[D' backward-char # Left arrow
+bindkey -M viins '^[[C' forward-char # Right arrow
+
+bindkey -M vicmd '^[[D' backward-char # Left arrow
+bindkey -M vicmd '^[[C' forward-char # Right arrow
+
# Define the 'autosuggest-execute' and 'autosuggest-accept' ZLE widgets
autoload -Uz autosuggest-execute autosuggest-accept
zle -N autosuggest-execute
zle -N autosuggest-accept
bindkey '^X' autosuggest-execute
bindkey '^Y' autosuggest-accept
+bindkey '\M-l' accept-and-complete-next-history
# Edit line in vim with alt-e
autoload edit-command-line; zle -N edit-command-line
@@ -37,3 +50,111 @@ bindkey '^[e' edit-command-line # alt + e
exit_zsh() { exit }
zle -N exit_zsh
bindkey '^D' exit_zsh
+
+# Copy/Paste
+# Safe clipboard copy
+smart_copy() {
+ local text="${LBUFFER}${RBUFFER}"
+
+ # Prefer Wayland, fallback to X11, then others
+ if command -v wl-copy >/dev/null 2>&1 && [[ "$WAYLAND_DISPLAY" || "$XDG_SESSION_TYPE" == "wayland" ]]; then
+ echo -n "$text" | wl-copy --foreground --type text/plain 2>/dev/null || true
+ elif command -v xclip >/dev/null 2>&1 && [[ "$DISPLAY" || "$XDG_SESSION_TYPE" == "x11" || "$XDG_SESSION_TYPE" == "x11-xwayland" ]]; then
+ echo -n "$text" | xclip -selection clipboard 2>/dev/null || true
+ elif [[ "$(uname -s)" == "Darwin" ]] && command -v pbcopy >/dev/null 2>&1; then
+ echo -n "$text" | pbcopy 2>/dev/null || true
+ elif [[ "$OSTYPE" == "cygwin" || "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
+ echo -n "$text" | clip.exe 2>/dev/null || true
+ else
+ echo "smart_copy: No supported clipboard utility found." >&2
+ fi
+}
+
+# Safe clipboard paste
+smart_paste() {
+ local clip=""
+ if command -v wl-paste >/dev/null 2>&1 && [[ "$WAYLAND_DISPLAY" || "$XDG_SESSION_TYPE" == "wayland" ]]; then
+ clip=$(wl-paste --no-newline 2>/dev/null)
+ elif command -v xclip >/dev/null 2>&1 && [[ "$DISPLAY" || "$XDG_SESSION_TYPE" == "x11" || "$XDG_SESSION_TYPE" == "x11-xwayland" ]]; then
+ clip=$(xclip -selection clipboard -o 2>/dev/null)
+ elif [[ "$(uname -s)" == "Darwin" ]] && command -v pbpaste >/dev/null 2>&1; then
+ clip=$(pbpaste 2>/dev/null)
+ elif [[ "$OSTYPE" == "cygwin" || "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
+ clip=$(powershell.exe -Command 'Get-Clipboard -Raw' 2>/dev/null | tr -d '\r')
+ else
+ echo "smart_paste: No supported clipboard utility found." >&2
+ fi
+
+ LBUFFER+="$clip"
+ zle reset-prompt
+}
+
+# Register widgets
+zle -N smart_copy
+zle -N smart_paste
+
+# Bind keys (optional: choose your preferred)
+bindkey '^V' smart_paste
+bindkey -M viins '^V' smart_paste
+bindkey -M vicmd '^V' smart_paste
+bindkey -M vicmd 'p' smart_paste
+
+bindkey '^Y' smart_copy
+bindkey -M viins '^Y' smart_copy
+bindkey -M vicmd '^Y' smart_copy
+bindkey -M vicmd 'y' smart_copy
+
+# In vi mode, map Alt-H and Alt-L
+#bindkey -M viins "^[u" go_up # Alt-H to go up
+#bindkey -M viins "^[o" go_into # Alt-L to go into a directory
+
+
+# Newline and clear
+function newline_clear() {
+ printf "\n"
+ command clear
+}
+
+zle -N newline_clear
+
+no_tmux_clear() {
+ zle clear-screen
+}
+zle -N no_tmux_clear
+
+# Newline before clear
+if [[ -n "$TMUX" ]]; then
+ # Bind Ctrl-L to send newline and clear screen
+ bindkey '^L' newline_clear
+else
+ bindkey '^L' no_tmux_clear
+fi
+
+# use ctrl-z to toggle in and out of bg
+function toggle_fg_bg() {
+ if [[ $#BUFFER -eq 0 ]]; then
+ BUFFER="fg"
+ zle accept-line
+ else
+ BUFFER=""
+ zle clear-screen
+ fi
+}
+zle -N toggle_fg_bg
+bindkey '^Z' toggle_fg_bg
+
+
+
+
+## Custom key bindings to control history behavior
+#bindkey -M vicmd '^[[C' vi-forward-char # Right arrow in normal mode - just move cursor
+#bindkey -M vicmd '^[[D' vi-backward-char # Left arrow in normal mode - just move cursor
+#bindkey -M vicmd '^A' beginning-of-line # Ctrl-A - go to beginning of line
+#bindkey -M vicmd '^E' end-of-line # Ctrl-E - go to end of line
+
+# Disable automatic suggestion accept on right arrow in normal mode
+
+## Additional vi-mode key bindings to prevent unwanted history completion
+## Disable automatic history completion in normal mode
+#bindkey -M vicmd '^[[C' vi-forward-char # Right arrow - just move right, don't complete
+#bindkey -M vicmd '^[[D' vi-backward-char # Left arrow - just move left
diff --git a/linux/home/.config/zsh/user/completion.zsh b/linux/home/.config/zsh/user/completion.zsh
index 7d9198a..cddf638 100644
--- a/linux/home/.config/zsh/user/completion.zsh
+++ b/linux/home/.config/zsh/user/completion.zsh
@@ -23,16 +23,34 @@ accept-and-complete-next-history() {
zle expand-or-complete-prefix
}
zle -N accept-and-complete-next-history
-bindkey -M menuselect '^i' accept-and-complete-next-history
+#bindkey -M menuselect '^i' accept-and-complete-next-history
bindkey '^n' expand-or-complete
bindkey '^p' reverse-menu-complete
+#bindkey '^I' expand-or-complete
+#bindkey '^[[Z]]' reverse-menu-complete
bindkey -M menuselect '^[' undo
#zstyle ':completion:*' menu select=1
#zstyle ':completion:*:directory-stack' list-colors '=(#b) #([0-9]#)*( *)==95=38;5;12'
# Options
-setopt COMPLETE_IN_WORD # Complete from both ends of a word.
+#setopt COMPLETE_IN_WORD # Complete from both ends of a word.
+##setopt ALWAYS_TO_END # Move cursor to the end of a completed word.
+##setopt PATH_DIRS # Perform path search even on command names with slashes.
+#setopt AUTO_MENU # Show completion menu on a successive tab press.
+#setopt AUTO_LIST # Automatically list choices on ambiguous completion.
+#setopt AUTO_PARAM_SLASH # If completed parameter is a directory, add a trailing slash.
+#setopt EXTENDED_GLOB # Needed for file modification glob modifiers with compinit.
+#unsetopt MENU_COMPLETE # Do not autoselect the first completion entry.
+
+## Disable all custom completions
+unsetopt COMPLETE_IN_WORD
+#unsetopt AUTO_MENU
+#unsetopt AUTO_LIST
+#unsetopt AUTO_PARAM_SLASH
+#unsetopt EXTENDED_GLOB
+#unsetopt MENU_COMPLETE # Do not autoselect the first completion entry.
+
setopt ALWAYS_TO_END # Move cursor to the end of a completed word.
setopt PATH_DIRS # Perform path search even on command names with slashes.
setopt AUTO_MENU # Show completion menu on a successive tab press.
@@ -41,6 +59,8 @@ setopt AUTO_PARAM_SLASH # If completed parameter is a directory, add a trail
setopt EXTENDED_GLOB # Needed for file modification glob modifiers with compinit.
unsetopt MENU_COMPLETE # Do not autoselect the first completion entry.
+# Test the behavior with just the basics
+compinit
# Variables
LS_COLORS=${LS_COLORS:-'di=34:ln=35:so=32:pi=33:ex=31:bd=36;01:cd=33;01:su=31;40;07:sg=36;40;07:tw=32;40;07:ow=33;40;07:'}
diff --git a/linux/home/.config/zsh/user/functions.zsh b/linux/home/.config/zsh/user/functions.zsh
index 75032ba..20e45bb 100644
--- a/linux/home/.config/zsh/user/functions.zsh
+++ b/linux/home/.config/zsh/user/functions.zsh
@@ -1,4 +1,263 @@
-## Function to temporarily unset GIT_WORK_TREE
+#if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
+# # bare repo for staging remote-friendly structure
+# _config() {
+# git --git-dir="$HOME/.cfg" --work-tree="$HOME/.cfg" "$@"
+# }
+#
+# # Determine OS
+# case "$(uname -s)" in
+# Linux) CFG_OS="linux" ;;
+# Darwin) CFG_OS="macos" ;;
+# MINGW*|MSYS*|CYGWIN*) CFG_OS="windows" ;;
+# *) CFG_OS="other" ;;
+# esac
+#
+# # Map local path to remote repo path
+# _repo_path() {
+# local f="$1"
+#
+# # Already repo-relative
+# case "$f" in
+# linux/*|macos/*|windows/*|common/*|profile/*) echo "$f"; return ;;
+# esac
+#
+# # Absolute path outside home → OS root mirror
+# if [[ "$f" = /* ]] && [[ "$f" != "$HOME"* ]]; then
+# echo "$CFG_OS/${f#/}"
+# return
+# fi
+#
+# # Home-relative → OS home
+# [[ "$f" = "$HOME"* ]] && f="${f#$HOME/}"
+# echo "$CFG_OS/home/$f"
+# }
+#
+# config() {
+# local cmd="$1"; shift
+# case "$cmd" in
+# add)
+# for f in "$@"; do
+# local repo_path="$(_repo_path "$f")"
+# local src="$f"
+#
+# # If user typed a relative path like .vi-mode, prepend $HOME
+# [[ "$src" != /* ]] && src="$HOME/$src"
+#
+# if [[ ! -e "$src" ]]; then
+# echo "File not found: $src"
+# continue
+# fi
+#
+# # Make parent dirs in bare repo for staging
+# mkdir -p "$HOME/.cfg/$(dirname "$repo_path")"
+# cp -a -- "$src" "$HOME/.cfg/$repo_path"
+#
+# # Stage in bare repo
+# _config add "$repo_path"
+# done
+# ;;
+# deploy)
+# for f in "$@"; do
+# local repo_path="$(_repo_path "$f")"
+# local sys_path
+# case "$repo_path" in
+# */home/*) sys_path="$HOME/${repo_path#*/home/}" ;;
+# *) sys_path="/${repo_path#$CFG_OS/}" ;;
+# esac
+# if [[ -e "$HOME/.cfg/$repo_path" ]]; then
+# echo "Deploying $repo_path → $sys_path"
+# sudo mkdir -p "$(dirname "$sys_path")"
+# sudo cp -a -- "$HOME/.cfg/$repo_path" "$sys_path"
+# else
+# echo "Not found in repo: $repo_path"
+# fi
+# done
+# ;;
+# rm|mv)
+# local mapped=()
+# for f in "$@"; do
+# mapped+=("$(_repo_path "$f")")
+# done
+# _config "$cmd" "${mapped[@]}"
+# ;;
+# *)
+# _config "$cmd" "$@"
+# ;;
+# esac
+# }
+#
+# # cache tracked files
+# cfg_files=$(_config ls-tree --name-only -r HEAD 2>/dev/null)
+# export CFG_FILES="$cfg_files"
+#fi
+
+
+if [[ -d "$HOME/.cfg" && -d "$HOME/.cfg/refs" ]]; then
+ export CFG_STAGE="$HOME/.local/share/dotfiles-stage"
+ mkdir -p "$CFG_STAGE"
+
+ _config() {
+ git --git-dir="$HOME/.cfg" --work-tree="$CFG_STAGE" "$@"
+ }
+
+ case "$(uname -s)" in
+ Linux) CFG_OS="linux" ;;
+ Darwin) CFG_OS="macos" ;;
+ MINGW*|MSYS*|CYGWIN*) CFG_OS="windows" ;;
+ *) CFG_OS="other" ;;
+ esac
+
+ _repo_path() {
+ local f="$1"
+ case "$f" in
+ linux/*|macos/*|windows/*|common/*|profile/*) echo "$f"; return ;;
+ esac
+ [[ "$f" = "$HOME"* ]] && f="${f#$HOME/}"
+ echo "$CFG_OS/home/$f"
+ }
+
+ config() {
+ local cmd="$1"; shift
+ case "$cmd" in
+ add)
+ for f in "$@"; do
+ local repo_path="$(_repo_path "$f")"
+ local src="$f"
+ [[ "$src" != /* ]] && src="$HOME/$src"
+ if [[ ! -e "$src" ]]; then
+ echo "File not found: $src"
+ continue
+ fi
+ mkdir -p "$CFG_STAGE/$repo_path"
+ cp -a -- "$src/." "$CFG_STAGE/$repo_path/"
+ _config add "$repo_path"
+ done
+ ;;
+ deploy)
+ for f in "$@"; do
+ local repo_path="$(_repo_path "$f")"
+ local sys_path
+ case "$repo_path" in
+ */home/*) sys_path="$HOME/${repo_path#*/home/}" ;;
+ *) sys_path="/${repo_path#$CFG_OS/}" ;;
+ esac
+ if [[ -e "$CFG_STAGE/$repo_path" ]]; then
+ echo "Deploying $repo_path → $sys_path"
+ sudo mkdir -p "$(dirname "$sys_path")"
+ sudo cp -a -- "$CFG_STAGE/$repo_path" "$sys_path"
+ else
+ echo "Not found in repo: $repo_path"
+ fi
+ done
+ ;;
+ pull-safe)
+ # Pull without overwriting local changes
+ _config fetch origin
+ _config merge -X ours origin/main
+ ;;
+ *)
+ _config "$cmd" "$@"
+ ;;
+ esac
+ }
+
+ cfg_files=$(_config ls-tree --name-only -r HEAD 2>/dev/null)
+ export CFG_FILES="$cfg_files"
+fi
+
+
+
+# Git
+## Use gh instead of git (fast GitHub command line client).
+#if type gh >/dev/null; then
+# alias git=gh
+# if type compdef >/dev/null 2>/dev/null; then
+# compdef gh=git
+# fi
+#fi
+#check_gh_installed() {
+# if command -v gh &> /dev/null; then
+# return 0 # gh is installed
+# else
+# return 1 # gh is not installed
+# fi
+#}
+#
+## Set alias for git to gh if gh is installed
+#if check_gh_installed; then
+# alias git=gh
+#fi
+
+# No arguments: `git status`
+# With arguments: acts like `git`
+g() {
+ if [ $# -gt 0 ]; then
+ git "$@" # If arguments are provided, pass them to git
+ else
+ git status # Otherwise, show git status
+ fi
+}
+
+# Complete g like git
+compdef g=git
+
+# Git alias commands
+ga() { g add "$@"; } # ga: Add files to the staging area
+gaw() { g add -A && g diff --cached -w | g apply --cached -R; } # gaw: Add all changes to the staging area and unstage whitespace changes
+grm() { g rm "$@"; }
+gb() { g branch "$@"; } # gb: List branches
+gbl() { g branch -l "$@"; } # gbl: List local branches
+gbD() { g branch -D "$@"; } # gbD: Delete a branch
+gbu() { g branch -u "$@"; } # gbu: Set upstream branch
+ge() { g clone "$@"; }
+gc() { g commit "$@"; } # gc: Commit changes
+gcm() { g commit -m "$@"; } # gcm: Commit with a message
+gca() { g commit -a "$@"; } # gca: Commit all changes
+gcaa() { g commit -a --amend "$@"; } # gcaa: Amend the last commit
+gcam() { g commit -a -m "$@"; } # gcam: Commit all changes with a message
+gce() { g commit -e "$@"; } # gce: Commit with message and allow editing
+gcfu() { g commit --fixup "$@"; } # gcfu: Commit fixes in the context of the previous commit
+gco() { g checkout "$@"; } # gco: Checkout a branch or file
+gcob() { g checkout -b "$@"; } # gcob: Checkout a new branch
+gcoB() { g checkout -B "$@"; } # gcoB: Checkout a new branch, even if it exists
+gcp() { g cherry-pick "$@"; } # gcp: Cherry-pick a commit
+gcpc() { g cherry-pick --continue "$@"; } # gcpc: Continue cherry-picking after resolving conflicts
+gd() { g diff "$@"; } # gd: Show changes
+#gd^() { g diff HEAD^ HEAD "$@"; } # gd^: Show changes between HEAD^ and HEAD
+gds() { g diff --staged "$@"; } # gds: Show staged changes
+gl() { g lg "$@"; } # gl: Show a customized log
+glg() { g log --graph --decorate --all "$@"; } # glg: Show a customized log with graph
+gls() { # Query `glog` with regex query.
+ query="$1"
+ shift
+ glog --pickaxe-regex "-S$query" "$@"
+}
+gdc() { g diff --cached "$@"; } # gdc: Show changes between the working directory and the index
+gu() { g pull "$@"} # gu: Pull
+gp() { g push "$@"} # gp: Push
+gpom() { g push origin main "$@"; } # gpom: Push changes to origin main
+gr() { g remote "$@"; } # gr: Show remote
+gra() { g rebase --abort "$@"; } # gra: Abort a rebase
+grb() { g rebase --committer-date-is-author-date "$@"; } # grb: Rebase with the author date preserved
+grbom() { grb --onto master "$@"; } # grbom: Rebase onto master
+grbasi() { g rebase --autosquash --interactive "$@"; } # grbasi: Interactive rebase with autosquash
+grc() { g rebase --continue "$@"; } # grc: Continue a rebase
+grs() { g restore --staged "$@"; } # grs: Restore changes staged for the next commit
+grv() { g remote -v "$@"; } # grv: Show remote URLs after each name
+grh() { g reset --hard "$@"; } # grh: Reset the repository and the working directory
+grH() { g reset HEAD "$@"; } # grH: Reset the index but not the working directory
+#grH^() { g reset HEAD^ "$@"; } # grH^: Reset the index and working directory to the state of the HEAD's first parent
+gs() { g status -sb "$@"; } # gs: Show the status of the working directory and the index
+gsd() { g stash drop "$@"; } # gsd: Drop a stash
+gsl() { g stash list --date=relative "$@"; } # gsl: List all stashes
+gsp() { g stash pop "$@"; } # gsp: Apply and remove a single stash
+gss() { g stash show "$@"; } # gss: Show changes recorded in the stash as a diff
+gst() { g status "$@"; } # gst: Show the status of the working directory and the index
+gsu() { g standup "$@"; } # gsu: Customized standup command
+gforgotrecursive() { g submodule update --init --recursive --remote "$@"; } # gforgotrecursive: Update submodules recursively
+gfp() { g commit --amend --no-edit && g push --force-with-lease "$@"; } # gfp: Amending the last commit and force-pushing
+
+# Temporarily unset GIT_WORK_TREE
function git_without_work_tree() {
# Only proceed if a git command is being run
if [ "$1" = "git" ]; then
@@ -21,44 +280,34 @@ function git_without_work_tree() {
}
# Set alias conditionally
-alias git='git_without_work_tree git'
+#alias git='git_without_work_tree git'
# Set bare dotfiles repository git environment variables dynamically
function set_git_env_vars() {
- # Check if the current command is a package manager command
+ # Do nothing unless ~/.cfg exists and is a bare git repo
+ [[ -d "$HOME/.cfg" ]] || return
+ git --git-dir="$HOME/.cfg" rev-parse --is-bare-repository &>/dev/null || return
+
+ # Skip if last command was a package manager
if [[ "${(%)${(z)history[1]}}" =~ ^(pacman|yay|apt|dnf|brew|npm|pip|gem|go|cargo) ]]; then
return
fi
- local git_dir="$(git rev-parse --git-dir -C . 2>/dev/null)"
- if [[ -n "$git_dir" ]]; then
- local is_bare="$(git -C "$git_dir" rev-parse --is-bare-repository 2>/dev/null)"
- if [[ "$is_bare" == "true" ]]; then
- export GIT_DIR="$HOME/.cfg"
- export GIT_WORK_TREE=$(realpath $(eval echo ~))
- else
- unset GIT_DIR
- unset GIT_WORK_TREE
- fi
+
+ # Only set env vars if not already inside another Git repo
+ if ! git rev-parse --is-inside-work-tree &>/dev/null; then
+ export GIT_DIR="$HOME/.cfg"
+ export GIT_WORK_TREE="$(realpath ~)"
else
- local root_dir="$(git rev-parse --show-toplevel 2>/dev/null)"
- if [[ -n "$root_dir" ]]; then
- unset GIT_DIR
- export GIT_WORK_TREE="$root_dir"
- else
- export GIT_DIR="$HOME/.cfg"
- export GIT_WORK_TREE=$(realpath $(eval echo ~))
- fi
+ unset GIT_DIR
+ unset GIT_WORK_TREE
fi
}
-# Define an auto_cd hook to automatically update Git environment variables
-#function chpwd() {
-# set_git_env_vars
-#}
-# Call the function to set Git environment variables when the shell starts up
+# Hook and initial call
+function chpwd() { set_git_env_vars }
set_git_env_vars
-
+# Git Subtrees
function gsp() {
# Config file for subtrees
#
@@ -108,13 +357,88 @@ getlast () {
fc -nl $((HISTCMD - 1))
}
+# Copy the current command to a file
+copy_command_to_file() {
+ # Only write the last command if BUFFER is not empty
+ if [[ -n "$BUFFER" ]]; then
+ echo "$BUFFER" > ~/command_log.txt # Overwrite with the latest command
+ else
+ # If the buffer is empty, remove the previous log file
+ command rm -f ~/command_log.txt # Optionally remove the log if no command is present
+ fi
+}
+
+# Display the latest command from the log in the user input
+display_latest_command() {
+ if [[ -f ~/command_log.txt ]]; then
+ # Read the last command from the log
+ local last_command
+ last_command=$(< ~/command_log.txt)
+
+ # Only display if the last command is not empty
+ if [[ -n "$last_command" ]]; then
+ BUFFER="$last_command" # Set the BUFFER to the last command
+ CURSOR=${#BUFFER} # Set the cursor to the end of the command
+ fi
+ fi
+ zle reset-prompt # Refresh the prompt
+}
+
+# Go up a directory
+go_up() {
+ copy_command_to_file # Copy the current command to a file
+ BUFFER="" # Clear the current command line
+ cd .. || return # Change directory and return if it fails
+ display_latest_command # Display the latest command in the user input
+}
+
+# Initialize a variable to store the previous directory
+previous_dir=""
+
+# Function to change directories
+go_into() {
+ copy_command_to_file # Copy the current command to a file
+
+ # Use fzf or another tool to choose the directory
+ local dir
+ dir=$( (ls -d */; echo "Go Last directory:") | fzf --height 40% --reverse --tac) # Include previous directory as an option
+
+ if [[ -n "$dir" ]]; then
+ # Check if the user selected the previous directory
+ if [[ "$dir" == Previous:* ]]; then
+ cd - || return # Change to the previous directory
+ else
+ cd "${dir%/}" || return # Change directory if a selection is made (remove trailing slash)
+ fi
+
+ # Save the current directory to previous_dir
+ previous_dir=$(pwd) # Update previous_dir to current directory after changing
+ BUFFER="" # Clear the current command line
+ display_latest_command # Display the last command if available
+ fi
+}
+
+# Register functions as ZLE widgets
+zle -N go_up
+zle -N go_into
+
+
+
+
# Enter directory and list contents
-#cd() { builtin cd $@ && lsd }
-cd() {
+function cd-clear-ls() {
if [ -n "$1" ]; then
- builtin cd "$@" && ls
+ builtin cd "$@" 2>/dev/null || { echo "cd: no such file or directory: $1"; return 1; }
else
- builtin cd ~ && ls
+ builtin cd ~ || return 1
+ fi
+
+ echo -e "\033[H\033[J" # Clear screen but keep scroll buffer
+
+ if [ "$PWD" != "$HOME" ] && git rev-parse --is-inside-work-tree &>/dev/null; then
+ ls -a
+ else
+ ls
fi
}
@@ -453,6 +777,63 @@ ssh-create() {
fi
}
+guest() {
+ local guest="$1"
+ shift
+
+ local port
+ if [[ "$#" -ge 2 && "${@: -1}" =~ ^[0-9]+$ ]]; then
+ port="${@: -1}"
+ set -- "${@:1:$(($#-1))}"
+ fi
+
+ if [[ -z "$guest" || "$#" -lt 1 ]]; then
+ echo "Send file(s) or directories to remote machine"
+ echo "Usage: guest <guest-alias> <file-or-directory>... [port]"
+ return 1
+ fi
+
+ # Auto-detect port
+ if [[ -z "$port" ]]; then
+ if nc -z localhost 22220 2>/dev/null; then
+ port=22220
+ elif nc -z localhost 22 2>/dev/null; then
+ port=22
+ else
+ echo "No known SSH port (22220 or 22) is open. Specify a port manually."
+ return 1
+ fi
+ fi
+
+ for src in "$@"; do
+ src="${src/#\~/$HOME}"
+ if [[ ! -e "$src" ]]; then
+ echo "Error: '$src' does not exist."
+ continue
+ fi
+
+ local abs_path dest_dir rel_dir rsync_src rsync_dest
+
+ abs_path=$(realpath "$src")
+ rel_dir="${abs_path#$HOME/}"
+ dest_dir=$(dirname "$rel_dir")
+
+ # Ensure target dir exists remotely
+ ssh -p "$port" "$guest" "mkdir -p ~/$dest_dir"
+
+ if [[ -d "$src" ]]; then
+ # Add trailing slash to copy contents instead of nesting the dir
+ rsync_src="${src%/}/"
+ rsync_dest="~/$rel_dir/"
+ else
+ rsync_src="$src"
+ rsync_dest="~/$dest_dir/"
+ fi
+
+ echo "Sending '$src' to '$guest:$rsync_dest'..."
+ rsync -avz -e "ssh -p $port" "$rsync_src" "$guest:$rsync_dest"
+ done
+}
historystat() {
history 0 | awk '{print $2}' | sort | uniq -c | sort -n -r | head
}
@@ -597,18 +978,6 @@ getstate () {
. ~/environment.tmp
}
-# use ctrl-z to toggle in and out of bg
-function toggle_fg_bg() {
- if [[ $#BUFFER -eq 0 ]]; then
- BUFFER="fg"
- zle accept-line
- else
- BUFFER=""
- zle clear-screen
- fi
-}
-zle -N toggle_fg_bg
-bindkey '^Z' toggle_fg_bg
# Tmux layout
openSession () {
@@ -636,13 +1005,15 @@ compress() {
fi
}
-# archive extract
extract() {
if [[ -f "$1" ]] ; then
- local filename=$(basename "$1")
- local foldername=${filename%%.*}
- local fullpath=$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$1")
+ local filename
+ filename=$(basename "$1")
+ local foldername="${filename%%.*}"
+ local fullpath
+ fullpath=$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$1")
local didfolderexist=false
+
if [[ -d "$foldername" ]]; then
didfolderexist=true
read -p "$foldername already exists, do you want to overwrite it? (y/n) " -n 1
@@ -651,24 +1022,29 @@ extract() {
return
fi
fi
- mkdir -p "$foldername" && cd "$foldername"
+
+ mkdir -p "$foldername" && cd "$foldername" || return
+
case "$1" in
*.tar.bz2) tar xjf "$fullpath" ;;
- *.tar.gz) tar xzf "$fullpath" ;;
- *.tar.xz) tar Jxvf "$fullpath" ;;
- *.tar.Z) tar xzf "$fullpath" ;;
- *.tar) tar xf "$fullpath" ;;
- *.taz) tar xzf "$fullpath" ;;
- *.tb2) tar xjf "$fullpath" ;;
- *.tbz) tar xjf "$fullpath" ;;
- *.tbz2) tar xjf "$fullpath" ;;
- *.tgz) tar xzf "$fullpath" ;;
- *.txz) tar Jxvf "$fullpath" ;;
- *.zip) unzip "$fullpath" ;;
- *) echo "'$1' cannot be extracted via extract()" \
+ *.tar.gz) tar xzf "$fullpath" ;;
+ *.tar.xz) tar Jxf "$fullpath" ;;
+ *.tar.Z) tar xzf "$fullpath" ;;
+ *.tar) tar xf "$fullpath" ;;
+ *.taz) tar xzf "$fullpath" ;;
+ *.tb2) tar xjf "$fullpath" ;;
+ *.tbz) tar xjf "$fullpath" ;;
+ *.tbz2) tar xjf "$fullpath" ;;
+ *.tgz) tar xzf "$fullpath" ;;
+ *.txz) tar Jxf "$fullpath" ;;
+ *.rar) unrar x -o+ "$fullpath" >/dev/null ;;
+ *.zip) unzip -o "$fullpath" ;;
+ *)
+ echo "'$1' cannot be extracted via extract()" \
&& cd .. \
&& ! "$didfolderexist" \
- && rm -r "$foldername" ;;
+ && rm -r "$foldername"
+ ;;
esac
else
echo "'$1' is not a valid file"
@@ -722,92 +1098,314 @@ trash() {
esac
}
-# Git
-## Use gh instead of git (fast GitHub command line client).
-#if type gh >/dev/null; then
-# alias git=gh
-# if type compdef >/dev/null 2>/dev/null; then
-# compdef gh=git
-# fi
-#fi
-#check_gh_installed() {
-# if command -v gh &> /dev/null; then
-# return 0 # gh is installed
-# else
-# return 1 # gh is not installed
-# fi
-#}
-#
-## Set alias for git to gh if gh is installed
-#if check_gh_installed; then
-# alias git=gh
-#fi
+what() {
+ type "$1"
+ echo "$PATH"
+}
-# No arguments: `git status`
-# With arguments: acts like `git`
-g() {
- if [ $# -gt 0 ]; then
- git "$@" # If arguments are provided, pass them to git
+shutdown() {
+ if [ "$#" -eq 0 ]; then
+ sudo /sbin/shutdown -h now
else
- git status # Otherwise, show git status
+ sudo /sbin/shutdown -h "$@"
fi
}
-# Complete g like git
-compdef g=git
+windowManagerName () {
+ local window=$(
+ xprop -root -notype
+ )
-# Define functions for common Git commands
-ga() { g add "$@"; } # ga: Add files to the staging area
-gaw() { g add -A && g diff --cached -w | g apply --cached -R; } # gaw: Add all changes to the staging area and unstage whitespace changes
-grm() { g rm "$@"; }
-gb() { g branch "$@"; } # gb: List branches
-gbl() { g branch -l "$@"; } # gbl: List local branches
-gbD() { g branch -D "$@"; } # gbD: Delete a branch
-gbu() { g branch -u "$@"; } # gbu: Set upstream branch
-ge() { g clone "$@"; }
-gc() { g commit "$@"; } # gc: Commit changes
-gcm() { g commit -m "$@"; } # gcm: Commit with a message
-gca() { g commit -a "$@"; } # gca: Commit all changes
-gcaa() { g commit -a --amend "$@"; } # gcaa: Amend the last commit
-gcam() { g commit -a -m "$@"; } # gcam: Commit all changes with a message
-gce() { g commit -e "$@"; } # gce: Commit with message and allow editing
-gcfu() { g commit --fixup "$@"; } # gcfu: Commit fixes in the context of the previous commit
-gco() { g checkout "$@"; } # gco: Checkout a branch or file
-gcob() { g checkout -b "$@"; } # gcob: Checkout a new branch
-gcoB() { g checkout -B "$@"; } # gcoB: Checkout a new branch, even if it exists
-gcp() { g cherry-pick "$@"; } # gcp: Cherry-pick a commit
-gcpc() { g cherry-pick --continue "$@"; } # gcpc: Continue cherry-picking after resolving conflicts
-gd() { g diff "$@"; } # gd: Show changes
-#gd^() { g diff HEAD^ HEAD "$@"; } # gd^: Show changes between HEAD^ and HEAD
-gds() { g diff --staged "$@"; } # gds: Show staged changes
-gl() { g lg "$@"; } # gl: Show a customized log
-glg() { g log --graph --decorate --all "$@"; } # glg: Show a customized log with graph
-gls() { # Query `glog` with regex query.
- query="$1"
- shift
- glog --pickaxe-regex "-S$query" "$@"
+ local identifier=$(
+ echo "${window}" |
+ awk '$1=="_NET_SUPPORTING_WM_CHECK:"{print $5}'
+ )
+
+ local attributes=$(
+ xprop -id "${identifier}" -notype -f _NET_WM_NAME 8t
+ )
+
+ local name=$(
+ echo "${attributes}" |
+ grep "_NET_WM_NAME = " |
+ cut --delimiter=' ' --fields=3 |
+ cut --delimiter='"' --fields=2
+ )
+
+ echo "${name}"
}
-gdc() { g diff --cached "$@"; } # gdc: Show changes between the working directory and the index
-gu() { g pull "$@"} # gu: Pull
-gp() { g push "$@"} # gp: Push
-gpom() { g push origin main "$@"; } # gpom: Push changes to origin main
-gr() { g remote "$@"; } # gr: Show remote
-gra() { g rebase --abort "$@"; } # gra: Abort a rebase
-grb() { g rebase --committer-date-is-author-date "$@"; } # grb: Rebase with the author date preserved
-grbom() { grb --onto master "$@"; } # grbom: Rebase onto master
-grbasi() { g rebase --autosquash --interactive "$@"; } # grbasi: Interactive rebase with autosquash
-grc() { g rebase --continue "$@"; } # grc: Continue a rebase
-grs() { g restore --staged "$@"; } # grs: Restore changes staged for the next commit
-grv() { g remote -v "$@"; } # grv: Show remote URLs after each name
-grh() { g reset --hard "$@"; } # grh: Reset the repository and the working directory
-grH() { g reset HEAD "$@"; } # grH: Reset the index but not the working directory
-#grH^() { g reset HEAD^ "$@"; } # grH^: Reset the index and working directory to the state of the HEAD's first parent
-gs() { g status -sb "$@"; } # gs: Show the status of the working directory and the index
-gsd() { g stash drop "$@"; } # gsd: Drop a stash
-gsl() { g stash list --date=relative "$@"; } # gsl: List all stashes
-gsp() { g stash pop "$@"; } # gsp: Apply and remove a single stash
-gss() { g stash show "$@"; } # gss: Show changes recorded in the stash as a diff
-gst() { g status "$@"; } # gst: Show the status of the working directory and the index
-gsu() { g standup "$@"; } # gsu: Customized standup command
-gforgotrecursive() { g submodule update --init --recursive --remote "$@"; } # gforgotrecursive: Update submodules recursively
-gfp() { g commit --amend --no-edit && g push --force-with-lease "$@"; } # gfp: Amending the last commit and force-pushing
+
+logout() {
+ local wm
+ wm="$(windowManagerName)"
+ if [[ -n "$wm" ]]; then
+ echo "Logging out by killing window manager: $wm"
+ pkill "$wm"
+ else
+ echo "No window manager detected!" >&2
+ fi
+}
+
+# Gentoo
+emg() {
+ if [[ -z "$1" ]]; then
+ echo "Usage: emg [USE_FLAGS] package [package...]"
+ return 1
+ fi
+
+ if [[ "$1" =~ ^[^-].* ]]; then
+ local use_flags="$1"
+ shift
+ sudo USE="$use_flags" emerge -av "$@"
+ else
+ sudo emerge -av "$@"
+ fi
+}
+
+# Remove command from history
+forget () { # Accepts one history line number as argument or search term
+ if [[ -z "$1" ]]; then
+ echo "Usage: hist <history_number> | hist -s <search_term>"
+ return 1
+ fi
+
+ if [[ "$1" == "-s" ]]; then
+ if [[ -z "$2" ]]; then
+ echo "Usage: hist -s <search_term>"
+ return 1
+ fi
+
+ local search_term="$2"
+
+ if [[ "$OSTYPE" == "linux-gnu"* ]]; then
+ LC_ALL=C sed -i "/${search_term}/d" "$HISTFILE" # GNU sed
+ else
+ LC_ALL=C sed -i '' "/${search_term}/d" "$HISTFILE" # BSD/macOS sed
+ fi
+
+ fc -R "$HISTFILE"
+ echo "Deleted all history entries matching '$search_term'."
+ else
+ local num=$1
+ local cmd=$(fc -ln $num $num 2>/dev/null)
+
+ if [[ -z "$cmd" ]]; then
+ echo "No history entry found for index $num"
+ return 1
+ fi
+
+ history -d $num
+
+ local escaped_cmd=$(echo "$cmd" | sed 's/[\/&]/\\&/g')
+
+ if [[ "$OSTYPE" == "linux-gnu"* ]]; then
+ LC_ALL=C sed -i "/${escaped_cmd}/d" "$HISTFILE"
+ else
+ LC_ALL=C sed -i '' "/${escaped_cmd}/d" "$HISTFILE"
+ fi
+
+ fc -R "$HISTFILE"
+ echo "Deleted '$cmd' from history."
+ fi
+}
+
+# Remove hist command itself
+remove_hist_command() {
+ [[ $1 != 'hist '* ]]
+}
+
+remove_hist_command
+
+
+search() {
+ # Search for a pattern in the specified directory (non-recursive).
+ dir="${1:-.}"
+ ls -1 "$dir" | grep -i "$2"
+}
+
+deepsearch() {
+ # Perform a recursive search for a pattern in the specified directory.
+ dir="${1:-.}"
+ find "$dir" -iname "$2"
+}
+
+notes() {
+ local base_dir="$HOME/documents/main/"
+
+ if [[ -z "$1" ]]; then
+ # No argument → cd to notes directory
+ cd "$base_dir" || return
+ return
+ fi
+
+ local target="$1" # The argument itself
+
+ # Use find to check if the file exists anywhere in the base directory
+ local found_files=($(find "$base_dir" -type f -name "$target"))
+
+ if [[ ${#found_files[@]} -eq 1 ]]; then
+ # Only one match found, open it directly
+ $EDITOR "${found_files[0]}"
+ elif [[ ${#found_files[@]} -gt 1 ]]; then
+ # Multiple files found, prompt the user to select one
+ echo "Multiple files found for '$target'. Please choose one:"
+ PS3="Please enter a number to select a file (1-${#found_files[@]}): "
+ select selected_file in "${found_files[@]}"; do
+ if [[ -n "$selected_file" ]]; then
+ $EDITOR "$selected_file"
+ break
+ else
+ echo "Invalid selection, try again."
+ fi
+ done
+ else
+ # If no match found, search for a directory
+ local found_dir=$(find "$base_dir" -type d -name "$target" -print -quit)
+
+ if [[ -n "$found_dir" ]]; then
+ # Directory found, cd into it
+ cd "$found_dir" || return
+ else
+ # If no match found, create the file and open it
+ local full_target="$base_dir/$target"
+ mkdir -p "$(dirname "$full_target")"
+ $EDITOR "$full_target"
+ fi
+ fi
+}
+
+# Enable tab completion for files and directories
+_notes_complete() {
+ local base_dir="$HOME/documents/main"
+ compadd -o nospace -- $(find "$base_dir" -type f -o -type d -printf '%P\n')
+}
+
+compdef _notes_complete notes
+
+
+ship() {
+ local binary_dir="$HOME/.local/share"
+ local bin_symlink_dir="$HOME/.local/bin"
+ local project_dirs=(
+ "$HOME/projects/"
+ "$HOME/src/"
+ "$HOME/src/site/"
+ )
+
+ mkdir -p "$binary_dir" "$bin_symlink_dir"
+
+ local project_dir=""
+
+ if [[ -n "$1" ]]; then
+ # Project name specified
+ for dir in "${project_dirs[@]}"; do
+ if [[ -d "$dir/$1" ]]; then
+ project_dir="$dir/$1"
+ break
+ fi
+ done
+
+ if [[ -z "$project_dir" ]]; then
+ echo "Project '$1' not found."
+ return 1
+ fi
+ else
+ # No argument: pick latest edited
+ local bin_file
+ bin_file=$(find "${project_dirs[@]}" -type f -name "Cargo.toml" -exec stat --format="%Y %n" {} \; 2>/dev/null | sort -nr | head -n1 | cut -d' ' -f2-)
+
+ if [[ -z "$bin_file" ]]; then
+ echo "No Cargo.toml found."
+ return 1
+ fi
+
+ project_dir=$(dirname "$bin_file")
+ fi
+
+ cd "$project_dir" || return
+ echo "Building project in $project_dir..."
+
+ # Build it
+ cargo build --release || { echo "Build failed"; return 1; }
+
+ # Assume binary has same name as project dir
+ local binary_name
+ binary_name=$(basename "$project_dir")
+ local built_binary="target/release/$binary_name"
+
+ if [[ -x "$built_binary" ]]; then
+ echo "Copying $built_binary to $binary_dir/$binary_name"
+ cp "$built_binary" "$binary_dir/$binary_name"
+
+ # Create/Update symlink
+ local symlink_path="$bin_symlink_dir/$binary_name"
+ ln -sf "$binary_dir/$binary_name" "$symlink_path"
+
+ echo "Binary is now at: $binary_dir/$binary_name"
+ echo "Symlink created at: $symlink_path"
+ else
+ echo "Built binary not found: $built_binary"
+ echo "You may need to manually specify the output binary."
+ fi
+}
+
+
+forge() {
+
+ local install=no
+ local usage="Usage: forge [--install]"
+
+ # Handle --install flag
+ if [[ "$1" == "--install" ]]; then
+ install=yes
+ shift
+ elif [[ "$1" == "-h" || "$1" == "--help" ]]; then
+ echo "$usage"
+ return 0
+ fi
+
+ if [[ -f "CMakeLists.txt" ]]; then
+ echo "📦 CMake project detected"
+ [[ ! -d build ]] && mkdir build
+ cmake -B build -DCMAKE_BUILD_TYPE=Release || return 1
+ cmake --build build || return 1
+ [[ "$install" == "yes" ]] && sudo cmake --install build
+
+ elif [[ -f "meson.build" ]]; then
+ echo "📦 Meson project detected"
+ if [[ ! -d build ]]; then
+ meson setup build || return 1
+ fi
+ ninja -C build || return 1
+ [[ "$install" == "yes" ]] && sudo ninja -C build install
+
+ elif [[ -f "Makefile" ]]; then
+ echo "📦 Makefile project detected"
+ # Try `make all`, fallback to `make` if `all` fails
+ if make -q all 2>/dev/null; then
+ make all || return 1
+ else
+ make || return 1
+ fi
+ [[ "$install" == "yes" ]] && sudo make install
+
+ else
+ echo "❌ No supported build system found."
+ return 1
+ fi
+}
+
+windows_home() {
+ for dir in /mnt/windows/Users/*(N); do
+ base=${dir:t} # `:t` is zsh's "tail" = basename
+ if [[ -d $dir && ! $base =~ ^(All Users|Default|Default User|Public|nx|desktop.ini)$ ]]; then
+ echo "$dir"
+ return 0
+ fi
+ done
+ return 1
+}
+
+if winhome_path=$(windows_home); then
+ hash -d winhome="$winhome_path"
+fi
diff --git a/linux/home/.config/zsh/user/options.zsh b/linux/home/.config/zsh/user/options.zsh
index 0e97fed..7115641 100644
--- a/linux/home/.config/zsh/user/options.zsh
+++ b/linux/home/.config/zsh/user/options.zsh
@@ -1,19 +1,19 @@
-
-# Let FZF use ripgrep by default
-if type rg &> /dev/null; then
- export FZF_DEFAULT_COMMAND='rg --files'
- export FZF_DEFAULT_OPTS='-m --height 50% --border'
-fi
+## Let FZF use ripgrep by default
+#if type rg &> /dev/null; then
+# export FZF_DEFAULT_COMMAND='rg --files'
+# export FZF_DEFAULT_OPTS='-m --height 50% --border'
+#fi
# Allow nnn filemanager to cd on quit
nnn() {
declare -x +g NNN_TMPFILE=$(mktemp --tmpdir $0.XXXX)
- trap "rm -f $NNN_TMPFILE" EXIT
+ trap "command rm -f $NNN_TMPFILE" EXIT
=nnn $@
[ -s $NNN_TMPFILE ] && source $NNN_TMPFILE
}
+FUNCNEST=999
# NVM
#nvm() {
@@ -48,3 +48,58 @@ if command -v kubectl > /dev/null; then
kubectl get deployment "$1" -o json | jq '.metadata.finalizers = null' | kubectl apply -f -
}
fi
+
+
+castero() {
+ if [[ -f ~/.local/share/venv/bin/activate ]]; then
+ . ~/.local/share/venv/bin/activate
+ fi
+ command castero "$@"
+}
+
+# --- Detect terminal control characters and behavior ---
+
+# Set these to true/false to run on every new tmux/terms
+: ${CHECK_ON_TMUX_CHANGES:=false}
+: ${CHECK_ON_NEW_INSTANCES:=false}
+
+# Fast terminal fingerprinting for optimizing prompt rendering
+function initialize_terminal_fingerprint() {
+ # --- Fast terminal fingerprint ---
+ TERM_BASIC="$TERM-$COLORTERM"
+ TERM_TMUX=""
+ [[ "$CHECK_ON_TMUX_CHANGES" == "true" && -n "$TMUX" ]] && TERM_TMUX="-tmux$TMUX_PANE"
+ TERM_INSTANCE=""
+ [[ "$CHECK_ON_NEW_INSTANCES" == "true" ]] && TERM_INSTANCE="-$$"
+ # Combine fingerprint parts only if they're non-empty (faster than function call)
+ CURRENT_TERM_FINGERPRINT="${TERM_BASIC}${TERM_TMUX}${TERM_INSTANCE}"
+ # Only run detection if terminal has changed (single comparison)
+ if [[ "$CURRENT_TERM_FINGERPRINT" != "$LAST_TERM_FINGERPRINT" ]]; then
+ export LAST_TERM_FINGERPRINT="$CURRENT_TERM_FINGERPRINT"
+ # Fast reset
+ export CTRL_C_SIGINT=false
+ export CTRL_V_PASTE=false
+ # Fast SIGINT check (single command, no pipes)
+ INTR_CHAR=$(stty -a 2>/dev/null | sed -n 's/.*intr = \([^;]*\);.*/\1/p' | tr -d ' ')
+ [[ "$INTR_CHAR" == "^C" ]] && export CTRL_C_SIGINT=true
+ # Check if Ctrl+V is bound to lnext terminal function
+ LNEXT_CHAR=$(stty -a 2>/dev/null | sed -n 's/.*lnext = \([^;]*\);.*/\1/p' | tr -d ' ')
+ # If lnext is NOT ^V, then Ctrl+V might work as paste
+ if [[ "$LNEXT_CHAR" != "^V" ]]; then
+ # Check if clipboard tools exist
+ if [[ -n "$WAYLAND_DISPLAY" && -x "$(command -v wl-paste)" ]]; then
+ export CTRL_V_PASTE=true
+ elif [[ -n "$DISPLAY" && -x "$(command -v xclip)" ]]; then
+ export CTRL_V_PASTE=true
+ fi
+ fi
+ # Print status only if debug is enabled
+ [[ -n "$DEBUG_TERM_DETECT" ]] && echo "Terminal: CTRL_C_SIGINT=$CTRL_C_SIGINT CTRL_V_PASTE=$CTRL_V_PASTE"
+ fi
+}
+
+# Initialize terminal fingerprint on startup
+initialize_terminal_fingerprint
+
+# Register Bun completion
+fpath=("$HOME/.bun" $fpath)
diff --git a/linux/home/.config/zsh/user/prompt.zsh b/linux/home/.config/zsh/user/prompt.zsh
index d9fc148..b8b1b97 100644
--- a/linux/home/.config/zsh/user/prompt.zsh
+++ b/linux/home/.config/zsh/user/prompt.zsh
@@ -2,19 +2,91 @@
########## Prompt(s) ##########
-terminfo_down_sc=$terminfo[cud1]$terminfo[cuu1]$terminfo[sc]$terminfo[cud1]
-
+# Autoload necessary functions for vcs_info and coloring
autoload -Uz vcs_info
autoload -Uz add-zsh-hook
autoload -U colors && colors
+# Enable prompt substitution
+setopt prompt_subst
+
+# Display git branch status and color
precmd_vcs_info() { vcs_info }
+# Add vcs_info to precmd functions
precmd_functions+=( precmd_vcs_info )
-setopt prompt_subst
+# Manipulates cursor position: moves down by 2 lines, saves position, and restores cursor after an operation.
+terminfo_down_sc=$terminfo[cud1]$terminfo[cuu1]$terminfo[sc]$terminfo[cud1]
+
+# Track last executed command for exit code display
+typeset -g _last_executed_command=""
+typeset -g _cmd_start_time=0
+typeset -g _cmd_end_time=0
+typeset -g _cmd_duration=0
+typeset -g _spinner_idx=0
+typeset -ga _spinner_frames=('⣾' '⣽' '⣻' '⢿' '⡿' '⣟' '⣯' '⣷')
+typeset -g _cmd_is_running=0
+typeset -g _show_spinner=0
+typeset -g _SPINNER_DELAY=5 # Only show spinner after 5 seconds
+typeset -g _FINISHED_DELAY=10 # Only show finished message after 10 seconds
+
+# Register the ZLE widget for spinner updates - do this early
+zle -N update_spinner
+
+# Cache git information to avoid repeated expensive operations
+typeset -g _git_cached_info=""
+typeset -g _git_cache_timestamp=0
+typeset -g _git_cache_lifetime=2 # seconds before cache expires
+
+# Calculate how much space is available for the prompt components
+function available_space() {
+ local width=${COLUMNS:-80}
+ echo $width
+}
+
+# Check if we need to abbreviate git info
+function need_to_abbreviate_git() {
+ local available=$(available_space)
+ local vi_mode_len=13 # Length of "-- INSERT --"
+ local prompt_base_len=20 # Base prompt elements length
+ local path_len=${#${PWD/#$HOME/\~}}
+ local git_full_len=0
+
+ # Try to estimate git info length if available
+ if git rev-parse --is-inside-work-tree &>/dev/null; then
+ local branch=$(git symbolic-ref --short HEAD 2>/dev/null)
+ git_full_len=${#branch}
+
+ # Add length for status indicators
+ if [[ -n "$(git status --porcelain)" ]]; then
+ # Rough estimate for status text
+ git_full_len=$((git_full_len + 20))
+ fi
+ fi
+
+ # Calculate total space needed
+ local total_needed=$((vi_mode_len + prompt_base_len + path_len + git_full_len))
+
+ # Determine if we need to abbreviate
+ if [[ $total_needed -gt $available ]]; then
+ return 0 # Need to abbreviate
+ else
+ return 1 # Don't need to abbreviate
+ fi
+}
+# Custom git branch coloring based on status
git_branch_test_color() {
+ local now=$(date +%s)
+ local cache_age=$((now - _git_cache_timestamp))
+
+ # Use cached value if available and not expired
+ if [[ -n "$_git_cached_info" && $cache_age -lt $_git_cache_lifetime ]]; then
+ echo "$_git_cached_info"
+ return
+ fi
+
local ref=$(git symbolic-ref --short HEAD 2> /dev/null)
if [ -n "${ref}" ]; then
if [ -n "$(git status --porcelain)" ]; then
@@ -22,76 +94,376 @@ git_branch_test_color() {
else
local gitstatuscolor='%F{82}'
fi
- echo "${gitstatuscolor}${ref}"
+ _git_cached_info="${gitstatuscolor}${ref}"
+ _git_cache_timestamp=$now
+ echo "$_git_cached_info"
else
+ _git_cached_info=""
+ _git_cache_timestamp=$now
echo ""
fi
}
+# Git branch with dynamic abbreviation
+git_branch_dynamic() {
+ local now=$(date +%s)
+ local cache_age=$((now - _git_cache_timestamp))
+
+ # Only query git if cache is expired
+ if [[ $cache_age -ge $_git_cache_lifetime ]]; then
+ local ref=$(git symbolic-ref --short HEAD 2> /dev/null)
+ if [ -n "${ref}" ]; then
+ if need_to_abbreviate_git; then
+ # Abbreviated version for small terminals
+ case "${ref}" in
+ "main") _git_cached_info="m" ;;
+ "master") _git_cached_info="m" ;;
+ "development") _git_cached_info="d" ;;
+ "develop") _git_cached_info="d" ;;
+ "feature/"*) _git_cached_info="f/${ref#feature/}" | cut -c 1-4 ;;
+ "release/"*) _git_cached_info="r/${ref#release/}" | cut -c 1-4 ;;
+ "hotfix/"*) _git_cached_info="h/${ref#hotfix/}" | cut -c 1-4 ;;
+ *) _git_cached_info="${ref}" | cut -c 1-5 ;; # Truncate to first 5 chars for other branches
+ esac
+ else
+ # Full branch name when there's room
+ _git_cached_info="${ref}"
+ fi
+ _git_cache_timestamp=$now
+ echo "$_git_cached_info"
+ else
+ _git_cached_info=""
+ _git_cache_timestamp=$now
+ echo ""
+ fi
+ else
+ echo "$_git_cached_info"
+ fi
+}
+
+# VCS info styles (e.g., git)
zstyle ':vcs_info:*' check-for-changes true
-zstyle ':vcs_info:*' stagedstr ' +%F{15}staged%f'
-zstyle ':vcs_info:*' unstagedstr ' -%F{15}unstaged%f'
-zstyle ':vcs_info:*' actionformats '%F{5}%F{2}%b%F{3}|%F{1}%a%F{5}%f '
-zstyle ':vcs_info:*' formats '%F{208} '$'\uE0A0'' %f$(git_branch_test_color)%f%F{76}%c%F{3}%u%f '
-zstyle ':vcs_info:git*+set-message:*' hooks git-untracked
zstyle ':vcs_info:*' enable git
+# Dynamically configure vcs_info formats based on available space
+function configure_vcs_styles() {
+ if need_to_abbreviate_git; then
+ # Abbreviated versions
+ zstyle ':vcs_info:*' stagedstr ' +%F{15}s%f'
+ zstyle ':vcs_info:*' unstagedstr ' -%F{15}u%f'
+ else
+ # Full versions
+ zstyle ':vcs_info:*' stagedstr ' +%F{15}staged%f'
+ zstyle ':vcs_info:*' unstagedstr ' -%F{15}unstaged%f'
+ fi
+
+ zstyle ':vcs_info:*' actionformats '%F{5}%F{2}%b%F{3}|%F{1}%a%F{5}%f '
+ zstyle ':vcs_info:*' formats '%F{208} '$'\uE0A0'' %f$(git_branch_test_color)%f%F{76}%c%F{3}%u%f '
+ zstyle ':vcs_info:git*+set-message:*' hooks git-untracked git-dynamic
+}
+
+# Show "untracked" status in git - with conditional abbreviation
+vi-git-untracked() {
if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == 'true' ]] && \
git status --porcelain | grep '??' &> /dev/null ; then
- hook_com[unstaged]+='%F{196} !%f%F{15}untracked%f'
+
+ if need_to_abbreviate_git; then
+ hook_com[unstaged]+='%F{196} !%f%F{15}u%f'
+ else
+ hook_com[unstaged]+='%F{196} !%f%F{15}untracked%f'
+ fi
fi
}
+# Dynamic git branch hook
++vi-git-dynamic() {
+ hook_com[branch]=$(git_branch_dynamic)
+}
+
+# SSH info with conditional abbreviation
ssh_name() {
if [[ -n $SSH_CONNECTION ]]; then
local ssh_info
- ssh_info="ssh:%F{green}%n$nc%f"
- if [[ -n $SSH_CONNECTION ]]; then
- local ip_address
- ip_address=$(echo $SSH_CONNECTION | awk '{print $3}')
- ssh_info="$ssh_info@%F{green}$ip_address%f"
+
+ if need_to_abbreviate_git; then
+ # Abbreviated SSH info
+ ssh_info="ssh:%F{green}%n$nc%f"
+ else
+ ssh_info="ssh:%F{green}%n$nc%f"
+ if [[ -n $SSH_CONNECTION ]]; then
+ local ip_address
+ ip_address=$(echo $SSH_CONNECTION | awk '{print $3}')
+ ssh_info="$ssh_info@%F{green}$ip_address%f"
+ fi
fi
echo " ${ssh_info}"
fi
}
+# Job names (for job control) with conditional abbreviation
function job_name() {
job_name=""
job_length=0
- if [ "${COLUMNS}" -gt 69 ]; then
- job_length=$((${COLUMNS}-70))
- [ "${job_length}" -lt "0" ] && job_length=0
- fi
+ local available=$(available_space)
- if [ "${job_length}" -gt 0 ]; then
+ # Only show jobs if we have reasonable space
+ if [ "${available}" -gt 60 ]; then
local job_count=$(jobs | wc -l)
if [ "${job_count}" -gt 0 ]; then
- local title_jobs="jobs:"
- job_name="${title_jobs}"
- job_name+="%F{green}$(jobs | grep + | tr -s " " | cut -d " " -f 4- | cut -b 1-${job_length} | sed "s/\(.*\)/\1/")%f"
+ if need_to_abbreviate_git; then
+ job_name+="%F{green}j:${job_count}%f"
+ else
+ local title_jobs="jobs:"
+ job_name="${title_jobs}"
+ job_length=$((${available}-70))
+ [ "${job_length}" -lt "0" ] && job_length=0
+
+ if [ "${job_length}" -gt 0 ]; then
+ job_name+="%F{green}$(jobs | grep + | tr -s " " | cut -d " " -f 4- | cut -b 1-${job_length} | sed "s/\(.*\)/\1/")%f"
+ else
+ job_name+="%F{green}${job_count}%f"
+ fi
+ fi
fi
fi
echo "${job_name}"
}
-function job_count() {
- local job_count
- job_count=$(jobs -s | grep -c "suspended")
- if [ "${job_count}" -gt 0 ]; then
- echo "(${job_count})"
+# Check if we should show the spinner based on elapsed time
+function should_show_spinner() {
+ if [[ $_cmd_is_running -eq 1 ]]; then
+ local current_time=$(date +%s)
+ local elapsed=$((current_time - _cmd_start_time))
+
+ # Show spinner only after delay threshold
+ if [[ $elapsed -ge $_SPINNER_DELAY ]]; then
+ _show_spinner=1
+ return 0 # Yes, show spinner
+ fi
+ fi
+
+ _show_spinner=0
+ return 1 # No, don't show spinner
+}
+
+# Update spinner animation - simplified version
+function update_spinner() {
+ # This function is now just a ZLE widget placeholder
+ # The actual spinner updates happen in the TRAPALRM handler
+ :
+}
+
+# Start spinner timer when command runs longer than threshold
+function start_spinner_timer() {
+ _spinner_idx=0
+ _cmd_is_running=1
+ _show_spinner=0 # Start with spinner hidden until delay passes
+
+ # Set up the TRAPALRM for periodic updates - CRITICAL FIX
+ TMOUT=0.5 # Update spinner every 0.5 seconds
+
+ # Define TRAPALRM function - this is key to the spinner working
+ TRAPALRM() {
+ if [[ $_cmd_is_running -eq 1 ]]; then
+ local current_time=$(date +%s)
+ local elapsed=$((current_time - _cmd_start_time))
+
+ # Show spinner only after delay threshold
+ if [[ $elapsed -ge $_SPINNER_DELAY ]]; then
+ _show_spinner=1
+ _spinner_idx=$(( (_spinner_idx + 1) % ${#_spinner_frames[@]} ))
+
+ # Force prompt refresh - critical for updating the spinner
+ if [[ -o zle ]]; then
+ zle reset-prompt 2>/dev/null || true
+ zle -R
+ fi
+ fi
+ fi
+ }
+}
+
+# Stop spinner when command finishes
+function stop_spinner_timer() {
+ _cmd_is_running=0
+ _show_spinner=0
+
+ # Disable the alarm trap and timer
+ TRAPALRM() { : }
+ TMOUT=0
+
+ # Force prompt refresh to clear spinner
+ if [[ -o zle ]]; then
+ zle reset-prompt 2>/dev/null || true
+ zle -R
fi
}
-current_jobs=' $(job_name)$(job_count)'
+# Format time in a human-readable way
+function format_time() {
+ local seconds=$1
+ local result=""
+
+ # Format time as hours:minutes:seconds for long durations
+ if [[ $seconds -ge 3600 ]]; then
+ local hours=$((seconds / 3600))
+ local minutes=$(( (seconds % 3600) / 60 ))
+ local secs=$((seconds % 60))
+ result="${hours}h${minutes}m${secs}s"
+ elif [[ $seconds -ge 60 ]]; then
+ local minutes=$((seconds / 60))
+ local secs=$((seconds % 60))
+ result="${minutes}m${secs}s"
+ else
+ result="${seconds}s"
+ fi
+
+ echo "$result"
+}
+
+# Error code display for RPROMPT with spinner - fixed version
+function exit_code_info() {
+ local exit_code=$?
+
+ # If a command is running and we should show spinner
+ if [[ $_cmd_is_running -eq 1 && $_show_spinner -eq 1 ]]; then
+ local spinner=${_spinner_frames[$_spinner_idx]}
+ local current_time=$(date +%s)
+ local elapsed=$((current_time - _cmd_start_time))
+ echo "%F{yellow}${spinner} ${elapsed}s%f"
+ return
+ fi
+
+ # Don't show error code when line editor is active (user is typing)
+ if [[ -o zle ]]; then
+ echo ""
+ return
+ fi
+
+ # Show command finished message for completed commands that took longer than threshold
+ if [[ -n "$_last_executed_command" && $_cmd_duration -ge $_FINISHED_DELAY ]]; then
+ local duration_formatted=$(format_time $_cmd_duration)
+
+ # Show error code along with finished message if there was an error
+ if [[ $exit_code -ne 0 ]]; then
+ # Show TSTP (148) as a suspension indicator instead of error
+ if [[ $exit_code -eq 148 ]]; then
+ echo "%F{cyan}finished ${duration_formatted}%f %F{yellow}⏸ TSTP%f"
+ return
+ fi
+
+ local signal_name=""
+ # Check if it's a signal
+ if [[ $exit_code -gt 128 && $exit_code -le 165 ]]; then
+ local signal_num=$((exit_code - 128))
+ signal_name=$(kill -l $signal_num 2>/dev/null)
+ if [[ -n "$signal_name" ]]; then
+ signal_name=" ($signal_name)"
+ fi
+ fi
+
+ # Return formatted error code with finished message
+ echo "%F{cyan}finished ${duration_formatted}%f %F{red}✘ $exit_code$signal_name%f"
+ else
+ echo "%F{cyan}finished ${duration_formatted}%f %F{green}✓%f"
+ fi
+ return
+ fi
+
+ # Don't show anything for exit code 0 (success) if this is first command
+ if [[ -z "$_last_executed_command" && $exit_code -eq 0 ]]; then
+ echo ""
+ return
+ fi
+
+ # Show TSTP (148) as a suspension indicator instead of error
+ if [[ $exit_code -eq 148 ]]; then
+ echo "%F{yellow}⏸ TSTP%f"
+ return
+ fi
+
+ if [[ $exit_code -ne 0 ]]; then
+ local signal_name=""
+
+ # Check if it's a signal
+ if [[ $exit_code -gt 128 && $exit_code -le 165 ]]; then
+ local signal_num=$((exit_code - 128))
+ signal_name=$(kill -l $signal_num 2>/dev/null)
+ if [[ -n "$signal_name" ]]; then
+ signal_name=" ($signal_name)"
+ fi
+ fi
+
+ # Return formatted error code
+ echo "%F{red}✘ $exit_code$signal_name%f"
+ else
+ echo "%F{green}✓%f" # Success indicator
+ fi
+}
+
+abbreviated_path() {
+ local full_path="${PWD/#$HOME/~}" # Replace $HOME with ~
+ local available=$(available_space)
+
+ # If path is root
+ if [[ "$full_path" == "/" ]]; then
+ echo "%F{4}/%f"
+ return
+ fi
+
+ # If path is just ~
+ if [[ "$full_path" == "~" ]]; then
+ echo "%F{4}~%f"
+ return
+ fi
+
+ # If extremely small terminal, show nothing to avoid breaking prompt
+ if (( available < 20 )); then
+ echo ""
+ return
+ fi
+
+ # For very narrow terminals, just show the current dir
+ if (( available < 30 )); then
+ echo "%F{4}%1~%f"
+ return
+ fi
+
+ # For moderately narrow terminals, show last two components
+ if (( available < 40 )); then
+ echo "%F{4}%2~%f"
+ return
+ fi
+
+ # For wide terminals, show full path
+ if (( available > 70 )); then
+ echo "%F{4}${full_path}%f"
+ return
+ fi
+
+ # Otherwise, show abbreviated path (e.g. ~/d/p/n)
+ local parts=("${(s:/:)full_path}")
+ local result=""
+ local last_index=${#parts[@]}
+
+ for i in {1..$((last_index - 1))}; do
+ [[ -n ${parts[i]} ]] && result+="/${parts[i]:0:1}"
+ done
+
+ result+="/${parts[last_index]}"
+ echo "%F{4}${result}%f"
+}
+
+
+# Prompt variables
user="%n"
at="%F{15}at%{$reset_color%}"
machine="%F{4}%m%{$reset_color%}"
relative_home="%F{4}%~%{$reset_color%}"
carriage_return=""$'\n'""
-empty_line_bottom="%r"
-chevron_right=""
+empty_line_bottom=""
+chevron_right=""
color_reset="%{$(tput sgr0)%}"
color_yellow="%{$(tput setaf 226)%}"
color_blink="%{$(tput blink)%}"
@@ -99,16 +471,27 @@ prompt_symbol="$"
dollar_sign="${color_yellow}${color_blink}${prompt_symbol}${color_reset}"
dollar="%(?:%F{2}${dollar_sign}:%F{1}${dollar_sign})"
space=" "
+#thin_space=$'\u2009'
+thin_space=$'\u202F'
cmd_prompt="%(?:%F{2}${chevron_right} :%F{1}${chevron_right} )"
git_info="\$vcs_info_msg_0_"
v1="%{┌─[%}"
v2="%{]%}"
v3="└─["
v4="]"
+newline=$'\n'
-function insert-mode () { echo "-- INSERT --" }
-function normal-mode () { echo "-- NORMAL --" }
+# Indicate INSERT mode for vi - NEVER truncate this
+function insert-mode () {
+ echo "-- INSERT --"
+}
+# Indicate NORMAL mode for vi - NEVER truncate this
+function normal-mode () {
+ echo "-- NORMAL --"
+}
+
+# Vi mode indicator
vi-mode-indicator () {
if [[ ${KEYMAP} == vicmd || ${KEYMAP} == vi-cmd-mode ]]; then
echo -ne '\e[1 q'
@@ -119,32 +502,87 @@ vi-mode-indicator () {
fi
}
-function set-prompt () {
+# Prompt function to ensure the prompt stays on one line, even in narrow terminals
+function set-prompt() {
vi-mode-indicator
- mode="%F{145}%{$terminfo_down_sc$vi_mode$terminfo[rc]%f%}"
- #PS1="${relative_home}${vcs_info_msg_0_}${current_jobs} ${carriage_return}${mode}${dollar}${space}"
- PS1="${v1}${user}${v2}${space}${relative_home}${vcs_info_msg_0_}${current_jobs}$(ssh_name) ${carriage_return}${mode}${v3}${dollar}${v4}${empty_line_bottom}"
- #RPROMPT="$(ssh_name)"
+ configure_vcs_styles # Dynamically set vcs styles based on available space
+ vcs_info # Refresh vcs info with new styles
+
+ local available=$(available_space)
+ if (( available < 14 )); then
+ # Extremely narrow terminal — use minimal prompt
+ PS1="${carriage_return}${dollar}${space}${empty_line_bottom}"
+ #RPROMPT='$(exit_code_info)'
+
+ else
+ # Path display - always show something for path, but adapt based on space
+ local path_display="$(abbreviated_path)"
+
+ # Git info - omit entirely if not enough space
+ local gitinfo=""
+ if [[ $available -gt 40 ]]; then
+ gitinfo="${vcs_info_msg_0_}"
+ fi
+
+ # Jobs info
+ local jobs=" $(job_name)"
+
+ # SSH info
+ local sshinfo="$(ssh_name)"
+
+ # Vi mode is priority 1 - ALWAYS show it
+ mode="%F{145}%{$terminfo_down_sc$vi_mode$terminfo[rc]%f%}"
+
+ # Right prompt for error codes or spinner
+ #RPROMPT='$(exit_code_info)'
+
+ PS1="${newline}${v1}${user}${v2} ${path_display}${gitinfo}${jobs}${sshinfo}${carriage_return}${mode}${v3}${dollar}${v4}${empty_line_bottom}"
+ fi
}
-precmd () {
- print -rP
+# Pre-command hook to set prompt
+my_precmd() {
+ # Calculate command duration if a command was run
+ if [[ -n "$_last_executed_command" && $_cmd_start_time -gt 0 ]]; then
+ _cmd_end_time=$(date +%s)
+ _cmd_duration=$((_cmd_end_time - _cmd_start_time))
+ else
+ _cmd_duration=0
+ fi
+
+ stop_spinner_timer # Make sure spinner is stopped
vcs_info
set-prompt
+ vi-mode-indicator
}
+add-zsh-hook precmd my_precmd
-function update-mode-file() {
+# Update mode file based on current mode
+update-mode-file() {
set-prompt
- local current_mode=$(cat ~/.vi-mode)
+ local current_mode=$(cat ~/.vi-mode 2>/dev/null || echo "")
local new_mode="$vi_mode"
+
if [[ "$new_mode" != "$current_mode" ]]; then
echo "$new_mode" >| ~/.vi-mode
fi
+
+ # Ensure we're in an interactive shell and ZLE is active
+ if [[ -o zle ]] && zle -l &>/dev/null; then
+ zle reset-prompt 2>/dev/null || true
+ else
+ # If ZLE is not active, fallback and print the prompt manually
+ set-prompt
+ print -Pn "$PS1"
+ fi
+
+ # Refresh tmux client if tmux is running
if command -v tmux &>/dev/null && [[ -n "$TMUX" ]]; then
tmux refresh-client -S
fi
}
+# Check if nvim is running and update mode
function check-nvim-running() {
if pgrep -x "nvim" > /dev/null; then
vi_mode=""
@@ -165,8 +603,10 @@ function check-nvim-running() {
fi
}
+# ZLE line initialization hook
function zle-line-init() {
zle reset-prompt
+ vi-mode-indicator
case "${KEYMAP}" in
vicmd)
echo -ne '\e[1 q'
@@ -177,9 +617,12 @@ function zle-line-init() {
esac
}
+# ZLE keymap select hook
function zle-keymap-select() {
update-mode-file
zle reset-prompt
+ zle -R
+ vi-mode-indicator
case "${KEYMAP}" in
vicmd)
echo -ne '\e[1 q'
@@ -190,13 +633,46 @@ function zle-keymap-select() {
esac
}
-preexec () { print -rn -- $terminfo[el]; echo -ne '\e[5 q' ; }
+# Safer version of zle reset-prompt
+function safe_reset_prompt() {
+ # Only reset if ZLE is active
+ if [[ -o zle ]] && zle -l &>/dev/null; then
+ zle reset-prompt 2>/dev/null || true
+ fi
+}
-zle -N zle-line-init
-zle -N zle-keymap-select
+# Preexec hook for command execution - NO BACKGROUND JOBS VERSION
+function preexec() {
+ # Store the command being executed
+ _last_executed_command=$1
+ _cmd_start_time=$(date +%s)
+ _cmd_is_running=1
+ _show_spinner=0 # Reset spinner flag
+
+ # Start the spinner timer immediately
+ start_spinner_timer
+ print -rn -- $terminfo[el]
+ echo -ne '\e[5 q'
+ vi-mode-indicator
+}
+
+# Terminal resizing: resets the prompt if ZLE is active, updates the mode file.
TRAPWINCH() {
- update-mode-file
+ if [[ -o zle ]] && zle -l &>/dev/null; then
+ zle -R
+ zle reset-prompt 2>/dev/null || true
+ fi
+ update-mode-file 2>/dev/null
}
+# Register ZLE hooks
+zle -N zle-line-init
+zle -N zle-keymap-select
+zle -N update_spinner
+
+# Register hooks
+add-zsh-hook preexec preexec
+add-zsh-hook precmd my_precmd
+
set-prompt
diff --git a/linux/home/.config/zsh/user/prompt_minimal.zsh b/linux/home/.config/zsh/user/prompt_minimal.zsh
new file mode 100644
index 0000000..0389e7d
--- /dev/null
+++ b/linux/home/.config/zsh/user/prompt_minimal.zsh
@@ -0,0 +1,295 @@
+# vim:ft=zsh ts=2 sw=2 sts=2
+#=#=#=
+# simle_is_power theme
+# folked from agnoster's Theme - https://gist.github.com/3712874
+#
+# In order for this theme to render correctly, you will need a
+# [Powerline-patched font](https://github.com/Lokaltog/powerline-fonts).
+#=#=
+#==============================================================================
+# Color setting {{{
+#==============================================================================
+
+setopt prompt_subst
+
+bg_dir=240
+bg_dark=237
+fg_red=210
+
+#===========================================================================}}}
+# Segment drawing {{{
+#==============================================================================
+# A few utility functions to make it easy and re-usable to draw segmented prompts
+
+CURRENT_BG='NONE'
+# SEGMENT_SEPARATOR=''
+SEGMENT_SEPARATOR=''
+# SEGMENT_SEPARATOR=''
+# SEGMENT_SEPARATOR='▒'
+# SEGMENT_SEPARATOR='▓▒░'
+
+# Begin a segment
+# Takes two arguments, background and foreground. Both can be omitted,
+# rendering default background/foreground.
+prompt_segment() {
+ local bg fg
+ [[ -n $1 ]] && bg="%K{$1}" || bg="%k"
+ [[ -n $2 ]] && fg="%F{$2}" || fg="%f"
+ if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then
+ echo -n " %{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%} "
+ else
+ echo -n "%{$bg%}%{$fg%} "
+ fi
+ CURRENT_BG=$1
+ [[ -n $3 ]] && echo -n $3
+}
+
+# End the prompt, closing any open segments
+prompt_end() {
+ if [[ -n $CURRENT_BG ]]; then
+ echo -n " %{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR"
+ else
+ echo -n "%{%k%}"
+ fi
+ echo -n "%{%f%}"
+ CURRENT_BG=''
+}
+
+#===========================================================================}}}
+# Prompt components {{{
+#==============================================================================
+# Each component will draw itself, and hide itself if no information needs to be shown
+#------------------------------------------------------------------------------
+# Init: {{{
+#------------------------------------------------------------------------------
+
+prompt_init() {
+ echo -n "%{%F{240}%K{240}%}"
+}
+
+#---------------------------------------------------------------------------}}}
+# Status: {{{
+#------------------------------------------------------------------------------
+# - was there an error
+# - am I root
+# - are there background jobs?
+# - am I in ranger subshell?
+
+prompt_status() {
+ local symbols
+ symbols=()
+ [[ $RETVAL -ne 0 ]] && symbols+="%{%F{${fg_red}}%}✞"
+ [[ $UID -eq 0 ]] && symbols+="%{%F{223}%}⚡"
+ [[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}⚙"
+ [[ -n ${RANGER_LEVEL} ]] && symbols+="%{%F{153}%}®"
+
+ [[ -n "$symbols" ]] && prompt_segment ${bg_dark} NONE "$symbols"
+}
+
+#---------------------------------------------------------------------------}}}
+# Virtualenv: current working virtualenv {{{
+#------------------------------------------------------------------------------
+
+prompt_virtualenv() {
+ local virtualenv_path="$VIRTUAL_ENV"
+ if [[ -n $virtualenv_path && -n $VIRTUAL_ENV_DISABLE_PROMPT ]]; then
+ prompt_segment green black "(`basename $virtualenv_path`)"
+ fi
+}
+
+#---------------------------------------------------------------------------}}}
+# Dir: current working directory {{{
+#------------------------------------------------------------------------------
+
+prompt_dir() {
+ prompt_segment ${bg_dir} 231 '%~'
+}
+
+#---------------------------------------------------------------------------}}}
+# Git: branch/detached head, dirty status {{{
+#------------------------------------------------------------------------------
+
+prompt_git() {
+ local ref dirty mode repo_path
+ repo_path=$(git rev-parse --git-dir 2>/dev/null)
+
+ if $(git rev-parse --is-inside-work-tree >/dev/null 2>&1); then
+ # dirty=$(parse_git_dirty)
+ ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➔ $(git show-ref --head -s --abbrev |head -n1 2> /dev/null)"
+ # if [[ -n $dirty ]]; then
+ # prompt_segment ${bg_dark} 223
+ # else
+ prompt_segment ${bg_dark} 153
+ # fi
+
+ if [[ -e "${repo_path}/BISECT_LOG" ]]; then
+ mode=" <B>"
+ elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then
+ mode=" >M<"
+ elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then
+ mode=" >R>"
+ fi
+
+ autoload -Uz vcs_info
+
+ zstyle ':vcs_info:*' enable git
+ zstyle ':vcs_info:*' get-revision true
+ zstyle ':vcs_info:*' check-for-changes true
+ zstyle ':vcs_info:*' stagedstr '+'
+ zstyle ':vcs_info:git:*' unstagedstr '*'
+ zstyle ':vcs_info:*' formats ' %u%c'
+ zstyle ':vcs_info:*' actionformats ' %u%c'
+ vcs_info
+ echo -n "${ref/refs\/heads\// }${vcs_info_msg_0_%% }${mode}"
+ fi
+}
+
+#---------------------------------------------------------------------------}}}
+# Hg: prompt {{{
+#------------------------------------------------------------------------------
+
+prompt_hg() {
+ local rev status
+ if $(hg id >/dev/null 2>&1); then
+ if $(hg prompt >/dev/null 2>&1); then
+ if [[ $(hg prompt "{status|unknown}") = "?" ]]; then
+ # if files are not added
+ prompt_segment ${fg_red} ${bg_dark}
+ st='±'
+ elif [[ -n $(hg prompt "{status|modified}") ]]; then
+ # if any modification
+ prompt_segment 223 ${bg_dark}
+ st='±'
+ else
+ # if working copy is clean
+ prompt_segment 153 ${bg_dark}
+ fi
+ echo -n $(hg prompt "☿ {rev}@{branch}") $st
+ else
+ st=""
+ rev=$(hg id -n 2>/dev/null | sed 's/[^-0-9]//g')
+ branch=$(hg id -b 2>/dev/null)
+ if `hg st | grep -q "^\?"`; then
+ prompt_segment ${fg_red} ${bg_dark}
+ st='±'
+ elif `hg st | grep -q "^(M|A)"`; then
+ prompt_segment 223 ${bg_dark}
+ st='±'
+ else
+ prompt_segment 153 ${bg_dark}
+ fi
+ echo -n "☿ $rev@$branch" $st
+ fi
+ fi
+}
+
+#}}}========================================================================}}}
+# Build main prompt {{{
+#==============================================================================
+
+
+function vi-mode-indicator() {
+ local current_mode
+ current_mode=$(cat ~/.vi-mode 2>/dev/null || echo "")
+
+ if [[ ${KEYMAP} == vicmd || ${KEYMAP} == vi-cmd-mode ]]; then
+ [[ "$current_mode" != "-- NORMAL --" ]] && echo "-- NORMAL --" >| ~/.vi-mode
+ elif [[ ${KEYMAP} == main || ${KEYMAP} == viins || ${KEYMAP} == '' ]]; then
+ [[ "$current_mode" != "-- INSERT --" ]] && echo "-- INSERT --" >| ~/.vi-mode
+ fi
+}
+
+build_prompt() {
+ RETVAL=$?
+ vi-mode-indicator
+ prompt_init
+ prompt_virtualenv
+ prompt_dir
+ prompt_git
+ prompt_hg
+ prompt_status
+ prompt_end
+}
+
+color_reset="%{$(tput sgr0)%}"
+color_yellow="%{$(tput setaf 226)%}"
+color_blink="%{$(tput blink)%}"
+prompt_symbol="$"
+dollar_sign="${color_yellow}${color_blink}${prompt_symbol}${color_reset}"
+dollar="%(?:%F{2}${dollar_sign}:%F{1}${dollar_sign})"
+
+v1="%{┌─[%}"
+v2="%{]%}"
+v3="└─["
+v4="]"
+user="%n"
+
+PROMPT="${v1}${user}%f%b%k${v2}$(build_prompt)$reset_color
+${v3}${dollar}${v4}${empty_line_bottom}$reset_color"
+#PROMPT='%n@%m:%~%# '
+#%{%F{240}%}\$ %{$reset_color%}'
+#%{${dollar}%} %{$reset_color%}'
+RPROMPT=''
+
+PROMPT2='%{%F{30}%}↪%{$reset_color%} '
+RPROMPT2='%{$fg_bold[green]%}%_%{$reset_color%}'
+
+function update-mode-file() {
+ local current_mode=$(cat ~/.vi-mode 2>/dev/null || echo "")
+ local new_mode="$vi_mode"
+
+ # Check if the mode is different before updating
+ if [[ "$new_mode" != "$current_mode" ]]; then
+ echo "$new_mode" >| ~/.vi-mode
+ fi
+
+ # Only call zle if ZLE is active
+ if [[ -o zle ]]; then
+ zle reset-prompt # Force refresh
+ fi
+
+ # Ensure tmux client refresh only happens if tmux is running
+ if command -v tmux &>/dev/null && [[ -n "$TMUX" ]]; then
+ tmux refresh-client -S
+ fi
+}
+function zle-line-init() {
+ zle reset-prompt
+ case "${KEYMAP}" in
+ vicmd)
+ echo -ne '\e[1 q'
+ ;;
+ main|viins|*)
+ echo -ne '\e[5 q'
+ ;;
+ esac
+}
+function zle-keymap-select() {
+ local current_keymap
+ current_keymap="${KEYMAP}"
+
+ update-mode-file
+ zle reset-prompt
+
+ case "$current_keymap" in
+ vicmd)
+ echo -ne '\e[1 q'
+ ;;
+ main|viins|*)
+ echo -ne '\e[5 q'
+ ;;
+ esac
+}
+
+precmd () {
+ print -rP
+}
+
+preexec () {
+ print -rn -- $terminfo[el]
+ echo -ne '\e[5 q' # Reset cursor shape
+}
+zle -N zle-line-init
+zle -N zle-keymap-select
+
+#===========================================================================}}}
diff --git a/linux/home/.config/zsh/user/prompt_new.zsh b/linux/home/.config/zsh/user/prompt_new.zsh
new file mode 100644
index 0000000..78791ef
--- /dev/null
+++ b/linux/home/.config/zsh/user/prompt_new.zsh
@@ -0,0 +1,863 @@
+# __ _ __ _| | _____ ______ _| | __
+# / _` |/ _` | |/ / _ \_ / _` | |/ /
+# | (_| | (_| | < (_) / / (_| | <
+# \__,_|\__, |_|\_\___/___\__,_|_|\_\
+# |___/
+#
+# An asynchronous, dynamic color prompt for ZSH with Git, vi mode, and exit
+# status indicators
+#
+#
+# MIT License
+#
+# Copyright (c) 2017-2019 Alexandros Kozak
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#
+# https://github.com/agkozak/agkozak-zsh-prompt
+#
+
+# shellcheck disable=SC1090,SC2016,SC2034,SC2088,SC2148,SC2154,SC2190
+
+# psvar[] Usage
+#
+# psvar Index Prompt String Equivalent Usage
+#
+# psvar[1] %1v Hostname/abbreviated hostname (only
+# displayed for SSH connections)
+# psvar[2] %2v Working directory or abbreviation
+# thereof
+# psvar[3] %3v Current working Git branch, along
+# with indicator of changes made
+# psvar[4] %4v Equals 'vicmd' when vi command mode
+# is enabled; otherwise empty
+
+# Set AGKOZAK_PROMPT_DEBUG=1 to see debugging information
+AGKOZAK_PROMPT_DEBUG=${AGKOZAK_PROMPT_DEBUG:-0}
+
+############################################################
+# Display a message on STDERR if debug mode is enabled
+#
+# Globals:
+# AGKOZAK_PROMPT_DEBUG
+# Arguments:
+# $1 Message to send to STDERR
+############################################################
+_agkozak_debug_print() {
+ (( AGKOZAK_PROMPT_DEBUG )) && print -- "agkozak-zsh-prompt: $1" >&2
+}
+
+if (( AGKOZAK_PROMPT_DEBUG )); then
+ autoload -Uz is-at-least
+
+ setopt WARN_CREATE_GLOBAL
+
+ if is-at-least 5.4.0; then
+ setopt WARN_NESTED_VAR
+ fi
+fi
+
+# Set AGKOZAK_PROMPT_DIRTRIM to the desired number of directory elements to
+# display, or set it to 0 for no directory trimming
+typeset -g AGKOZAK_PROMPT_DIRTRIM=${AGKOZAK_PROMPT_DIRTRIM:-2}
+
+# Set AGKOZAK_MULTILINE to 0 to enable the legacy, single-line prompt
+typeset -g AGKOZAK_MULTILINE=${AGKOZAK_MULTILINE:-1}
+
+# Set AGKOZAK_LEFT_PROMPT_ONLY to have the Git status appear in the left prompt
+typeset -g AGKOZAK_LEFT_PROMPT_ONLY=${AGKOZAK_LEFT_PROMPT_ONLY:-0}
+
+# Set AGKOZAK_COLORS_* variables to any valid color
+# AGKOZAK_COLORS_EXIT_STATUS changes the exit status color (default: red)
+# AGKOZAK_COLORS_USER_HOST changes the username/hostname color (default: green)
+# AGKOZAK_COLORS_PATH changes the path color (default: blue)
+# AGKOZAK_COLORS_BRANCH_STATUS changes the branch status color (default: yellow)
+# AGKOZAK_COLORS_PROMPT_CHAR changes the prompt character color (default: white)
+typeset -g AGKOZAK_COLORS_EXIT_STATUS=${AGKOZAK_COLORS_EXIT_STATUS:-red}
+typeset -g AGKOZAK_COLORS_USER_HOST=${AGKOZAK_COLORS_USER_HOST:-green}
+typeset -g AGKOZAK_COLORS_PATH=${AGKOZAK_COLORS_PATH:-blue}
+typeset -g AGKOZAK_COLORS_BRANCH_STATUS=${AGKOZAK_COLORS_BRANCH_STATUS:-yellow}
+
+setopt PROMPT_SUBST NO_PROMPT_BANG
+
+######################################################################
+# GENERAL FUNCTIONS
+######################################################################
+
+############################################################
+# Are colors available?
+#
+# Globals:
+# AGKOZAK_HAS_COLORS
+############################################################
+_agkozak_has_colors() {
+ if (( $+AGKOZAK_HAS_COLORS )); then
+ :
+ else
+ case $TERM in
+ *-256color) typeset -g AGKOZAK_HAS_COLORS=1 ;;
+ vt100|dumb) typeset -g AGKOZAK_HAS_COLORS=0 ;;
+ *)
+ local colors
+ case $OSTYPE in
+ freebsd*|dragonfly*) colors=$(tput Co) ;;
+ *) colors=$(tput colors) ;;
+ esac
+ typeset -g AGKOZAK_HAS_COLORS=$(( colors >= 8 ))
+ ;;
+ esac
+ fi
+ (( AGKOZAK_HAS_COLORS ))
+}
+
+############################################################
+# Is the user connected via SSH?
+#
+# This function works perfectly for regular users. It is
+# nearly impossible to detect with accuracy how a superuser
+# is connected, so this prompt opts simply to display his or
+# her username and hostname in inverse video.
+############################################################
+_agkozak_is_ssh() {
+ [[ -n "${SSH_CONNECTION-}${SSH_CLIENT-}${SSH_TTY-}" ]]
+}
+
+############################################################
+# Emulation of bash's PROMPT_DIRTRIM for ZSH
+#
+# Take PWD and substitute HOME with `~'. If the rest of PWD
+# has more than a certain number of elements in its
+# directory tree, keep the number specified by
+# AGKOZAK_PROMPT_DIRTRIM (default: 2) and abbreviate the
+# rest with `...'. (Set AGKOZAK_PROMPT_DIRTRIM=0 to disable
+# directory trimming). For example,
+#
+# $HOME/dotfiles/polyglot/img
+#
+# will be displayed as
+#
+# ~/.../polyglot/img
+#
+# Named directories will by default be displayed using their
+# aliases in the prompt (e.g. `~project'). Set
+# AGKOZAK_NAMED_DIRS=0 to have them displayed just like any
+# other directory.
+#
+# Globals:
+# AGKOZAK_NAMED_DIRS
+# Arguments:
+# $@ [Optional] If `-v', store the function's output in
+# psvar[2] instead of printing it to STDOUT
+# $@ Number of directory elements to display (default: 2)
+############################################################
+_agkozak_prompt_dirtrim() {
+ # Process arguments
+ local argument
+ for argument in $@; do
+ [[ $argument == '-v' ]] && local var=1
+ done
+ until [[ $1 != '-v' ]]; do
+ shift
+ done
+ [[ $1 -ge 0 ]] || set 2
+
+ # Default behavior (when AGKOZAK_NAMED_DIRS is 1)
+ typeset -g AGKOZAK_NAMED_DIRS=${AGKOZAK_NAMED_DIRS:-1}
+ if (( AGKOZAK_NAMED_DIRS )); then
+ local zsh_pwd
+ print -Pnz '%~'
+
+ # IF AGKOZAK_PROMPT_DIRTRIM is not 0, trim directory
+ if (( $1 )); then
+ read -rz zsh_pwd
+ case $zsh_pwd in
+ \~) print -Pnz $zsh_pwd ;;
+ \~/*) print -Pnz "%($(( $1 + 2 ))~|~/.../%${1}~|%~)" ;;
+ \~*) print -Pnz "%($(( $1 + 2 ))~|${zsh_pwd%%${zsh_pwd#\~*\/}}.../%${1}~|%~)" ;;
+ *) print -Pnz "%($(( $1 + 1 ))/|.../%${1}d|%d)" ;;
+ esac
+ fi
+
+ # If AGKOZAK_NAMED_DIRS is 0
+ else
+ local dir dir_count
+ case $HOME in
+ /) dir=${PWD} ;;
+ *) dir=${PWD#$HOME} ;;
+ esac
+
+ # If AGKOZAK_PROMPT_DIRTRIM is not 0, trim the directory
+ if (( $1 > 0 )); then
+
+ # The number of directory elements is the number of slashes in ${PWD#$HOME}
+ dir_count=$(( ${#dir} - ${#${dir//\//}} ))
+ if (( dir_count <= $1 )); then
+ case $PWD in
+ ${HOME}) print -nz '~' ;;
+ ${HOME}*) print -nz "~${dir}" ;;
+ *) print -nz "$PWD" ;;
+ esac
+ else
+ local lopped_path i
+ lopped_path=${dir}
+ i=0
+ while (( i != $1 )); do
+ lopped_path=${lopped_path%\/*}
+ (( i++ ))
+ done
+ case $PWD in
+ ${HOME}*) print -nz "~/...${dir#${lopped_path}}" ;;
+ *) print -nz -f '...%s' "${PWD#${lopped_path}}" ;;
+ esac
+ fi
+
+ # If AGKOZAK_PROMPT_DIRTRIM is 0
+ else
+ case $PWD in
+ ${HOME}) print -nz '~' ;;
+ ${HOME}*) print -nz "~${dir}" ;;
+ *) print -nz "$PWD" ;;
+ esac
+ fi
+ fi
+
+ local output
+ read -rz output
+
+ # Argument -v stores the output to psvar[2]; otherwise send to STDOUT
+ if (( var )); then
+ psvar[2]=$output
+ else
+ print $output
+ fi
+}
+
+############################################################
+# Display current branch name, followed by symbols
+# representing changes to the working copy
+############################################################
+_agkozak_branch_status() {
+ local ref branch
+ ref=$(command git symbolic-ref --quiet HEAD 2> /dev/null)
+ case $? in # See what the exit code is.
+ 0) ;; # $ref contains the name of a checked-out branch.
+ 128) return ;; # No Git repository here.
+ # Otherwise, see if HEAD is in detached state.
+ *) ref=$(command git rev-parse --short HEAD 2> /dev/null) || return ;;
+ esac
+ branch=${ref#refs/heads/}
+
+ if [[ -n $branch ]]; then
+ local git_status symbols i=1 k
+ git_status="$(LC_ALL=C command git status 2>&1)"
+
+ typeset -A messages
+ messages=(
+ '&*' ' have diverged,'
+ '&' 'Your branch is behind '
+ '*' 'Your branch is ahead of '
+ '+' 'new file: '
+ 'x' 'deleted: '
+ '!' 'modified: '
+ '>' 'renamed: '
+ '?' 'Untracked files:'
+ )
+
+ for k in '&*' '&' '*' '+' 'x' '!' '>' '?'; do
+ case $git_status in
+ *${messages[$k]}*) symbols+="${AGKOZAK_CUSTOM_SYMBOLS[$i]:-$k}" ;;
+ esac
+ (( i++ ))
+ done
+
+ [[ -n $symbols ]] && symbols=" ${symbols}"
+
+ printf '%s(%s%s)' "${AGKOZAK_BRANCH_STATUS_SEPARATOR- }" "$branch" "$symbols"
+ fi
+}
+
+############################################################
+# Redraw the prompt when the vi mode changes. When the user
+# enters vi command mode, the % or # in the prompt changes
+# to a colon
+############################################################
+zle-keymap-select() {
+ [[ $KEYMAP == 'vicmd' ]] && psvar[4]='vicmd' || psvar[4]=''
+ zle reset-prompt
+ zle -R
+}
+
+############################################################
+# Redraw prompt when terminal size changes
+############################################################
+TRAPWINCH() {
+ zle && zle -R
+}
+
+############################################################
+# For legacy custom prompts: print a vi mode indicator
+############################################################
+_agkozak_vi_mode_indicator() {
+ case $KEYMAP in
+ vicmd) print -n ':' ;;
+ *) print -n '%#' ;;
+ esac
+}
+
+######################################################################
+# ASYNCHRONOUS FUNCTIONS
+######################################################################
+
+# Standarized $0 handling
+# (See https://github.com/zdharma/Zsh-100-Commits-Club/blob/master/Zsh-Plugin-Standard.adoc)
+0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}"
+typeset -g AGKOZAK_PROMPT_DIR="${0:A:h}"
+
+############################################################
+# If zsh-async has not already been loaded, try to load it
+#
+# Globals:
+# AGKOZAK_PROMPT_DEBUG
+# AGKOZAK_PROMPT_DIR
+############################################################
+_agkozak_load_async_lib() {
+ if ! whence -w async_init &> /dev/null; then # Don't load zsh-async twice
+ if (( AGKOZAK_PROMPT_DEBUG )); then
+ source "${AGKOZAK_PROMPT_DIR}/lib/async.zsh"
+ else
+ source "${AGKOZAK_PROMPT_DIR}/lib/async.zsh" &> /dev/null
+ fi
+ local success=$?
+ return $success
+ fi
+}
+
+############################################################
+# If SIGUSR1 is available and not already in use by ZSH, use
+# it; otherwise disable asynchronous mode
+############################################################
+_agkozak_has_usr1() {
+ if whence -w TRAPUSR1 &> /dev/null; then
+ _agkozak_debug_print 'TRAPUSR1 already defined.'
+ return 1
+ else
+ case $signals in # Array containing names of available signals
+ *USR1*) return 0 ;;
+ *)
+ _agkozak_debug_print 'SIGUSR1 not available.'
+ return 1
+ ;;
+ esac
+ fi
+}
+
+############################################################
+# If AGKOZAK_FORCE_ASYNC_METHOD is set to a valid value,
+# set AGKOZAK_ASYNC_METHOD to that; otherwise, determine
+# the optimal asynchronous method from the environment (usr1
+# for MSYS2/Cygwin, zsh-async for WSL, subst-async for
+# everything else), with fallbacks being available. Define
+# the necessary asynchronous functions (loading async.zsh
+# when necessary).
+#
+# Globals:
+# AGKOZAK_IS_WSL
+# AGKOZAK_ASYNC_METHOD
+# AGKOZAK_FORCE_ASYNC_METHOD
+# AGKOZAK_TRAPUSR1_FUNCTION
+############################################################
+_agkozak_async_init() {
+
+ # WSL should have BG_NICE disabled, since it does not have a Linux kernel
+ setopt LOCAL_OPTIONS EXTENDED_GLOB
+ if [[ -e /proc/version ]]; then
+ if [[ -n ${(M)${(f)"$(</proc/version)"}:#*Microsoft*} ]]; then
+ unsetopt BG_NICE
+ typeset -g AGKOZAK_IS_WSL=1 # For later reference
+ fi
+ fi
+
+ # If AGKOZAK_FORCE_ASYNC_METHOD is set, force the asynchronous method
+ [[ $AGKOZAK_FORCE_ASYNC_METHOD == 'zsh-async' ]] && _agkozak_load_async_lib
+ if [[ $AGKOZAK_FORCE_ASYNC_METHOD == (subst-async|zsh-async|usr1|none) ]]; then
+ typeset -g AGKOZAK_ASYNC_METHOD=$AGKOZAK_FORCE_ASYNC_METHOD
+
+ # Otherwise, first provide for certain quirky systems
+ else
+
+ if (( AGKOZAK_IS_WSL )) || [[ $OSTYPE == solaris* ]]; then
+ if [[ $ZSH_VERSION != '5.0.2' ]] &&_agkozak_load_async_lib; then
+ typeset -g AGKOZAK_ASYNC_METHOD='zsh-async'
+ elif _agkozak_has_usr1; then
+ typeset -g AGKOZAK_ASYNC_METHOD='usr1'
+ else
+ typeset -g AGKOZAK_ASYNC_METHOD='subst-async'
+ fi
+
+ # SIGUSR1 method is still much faster on MSYS2, Cygwin, and ZSH v5.0.2
+ elif [[ $ZSH_VERSION == '5.0.2' ]] || [[ $OSTYPE == (msys|cygwin) ]]; then
+ if _agkozak_has_usr1; then
+ typeset -g AGKOZAK_ASYNC_METHOD='usr1'
+ else
+ typeset -g AGKOZAK_ASYNC_METHOD='subst-async'
+ fi
+
+ # Asynchronous methods don't work in Emacs shell mode (but they do in term
+ # and ansi-term)
+ elif [[ $TERM == 'dumb' ]]; then
+ typeset -g AGKOZAK_ASYNC_METHOD='none'
+
+ # Otherwise use subst-async
+ else
+ typeset -g AGKOZAK_ASYNC_METHOD='subst-async'
+ fi
+ fi
+
+ ############################################################
+ # Process substitution async method
+ #
+ # Fork a background process to fetch the Git status and feed
+ # it asynchronously to a file descriptor. Install a callback
+ # handler to process input from the file descriptor.
+ #
+ # Globals:
+ # AGKOZAK_ASYNC_FD
+ # AGKOZAK_IS_WSL
+ ############################################################
+ _agkozak_subst_async() {
+ setopt LOCAL_OPTIONS NO_IGNORE_BRACES
+ typeset -g AGKOZAK_ASYNC_FD=13371
+
+ # Workaround for buggy behavior in MSYS2, Cygwin, and Solaris
+ if [[ $OSTYPE == (msys|cygwin|solaris*) ]]; then
+ exec {AGKOZAK_ASYNC_FD}< <(_agkozak_branch_status; command true)
+ # Prevent WSL from locking up when using X; also workaround for ZSH v5.0.2
+ elif (( AGKOZAK_IS_WSL )) && (( $+DISPLAY )) \
+ || [[ $ZSH_VERSION == '5.0.2' ]]; then
+ exec {AGKOZAK_ASYNC_FD}< <(_agkozak_branch_status)
+ command sleep 0.01
+ else
+ exec {AGKOZAK_ASYNC_FD}< <(_agkozak_branch_status)
+ fi
+
+ # Bug workaround; see http://www.zsh.org/mla/workers/2018/msg00966.html
+ command true
+
+ zle -F "$AGKOZAK_ASYNC_FD" _agkozak_zsh_subst_async_callback
+ }
+
+ ############################################################
+ # ZLE callback handler
+ #
+ # Read Git status from file descriptor and set psvar[3]
+ #
+ # Arguments:
+ # $1 File descriptor
+ ############################################################
+ _agkozak_zsh_subst_async_callback() {
+ setopt LOCAL_OPTIONS NO_IGNORE_BRACES
+
+ local FD="$1" response
+
+ # Read data from $FD descriptor
+ IFS='' builtin read -rs -d $'\0' -u "$FD" response
+
+ # Withdraw callback and close the file descriptor
+ zle -F ${FD}; exec {FD}<&-
+
+ # Make the changes visible
+ psvar[3]="$response"
+ zle && zle reset-prompt
+ }
+
+ case $AGKOZAK_ASYNC_METHOD in
+
+ zsh-async)
+
+ ############################################################
+ # Create zsh-async worker
+ ############################################################
+ _agkozak_zsh_async() {
+ async_start_worker agkozak_git_status_worker -n
+ async_register_callback agkozak_git_status_worker _agkozak_zsh_async_callback
+ async_job agkozak_git_status_worker _agkozak_branch_status
+ }
+
+ ############################################################
+ # Set RPROMPT and stop worker
+ ############################################################
+ _agkozak_zsh_async_callback() {
+ psvar[3]=$3
+ zle && zle reset-prompt
+ async_stop_worker agkozak_git_status_worker -n
+ }
+ ;;
+
+ usr1)
+
+ ############################################################
+ # Launch async workers to calculate Git status. TRAPUSR1
+ # actually displays the status; if some other script
+ # redefines TRAPUSR1, drop the prompt into synchronous mode.
+ #
+ # Globals:
+ # AGKOZAK_TRAPUSR1_FUNCTION
+ # AGKOZAK_USR1_ASYNC_WORKER
+ # AGKOZAK_ASYNC_METHOD
+ ############################################################
+ _agkozak_usr1_async() {
+ if [[ "$(builtin which TRAPUSR1)" = "$AGKOZAK_TRAPUSR1_FUNCTION" ]]; then
+ # Kill running child process if necessary
+ if (( AGKOZAK_USR1_ASYNC_WORKER )); then
+ kill -s HUP "$AGKOZAK_USR1_ASYNC_WORKER" &> /dev/null || :
+ fi
+
+ # Start background computation of Git status
+ _agkozak_usr1_async_worker &!
+ typeset -g AGKOZAK_USR1_ASYNC_WORKER=$!
+ else
+ _agkozak_debug_print 'TRAPUSR1 has been redefined. Switching to subst-async mode.'
+ typeset -g AGKOZAK_ASYNC_METHOD='subst-async'
+ psvar[3]="$(_agkozak_branch_status)"
+ fi
+ }
+
+ ############################################################
+ # Calculate Git status and store it in a temporary file;
+ # then kill own process, sending SIGUSR1
+ #
+ # Globals:
+ # AGKOZAK_PROMPT_DEBUG
+ ############################################################
+ _agkozak_usr1_async_worker() {
+ # Save Git branch status to temporary file
+ _agkozak_branch_status >| /tmp/agkozak_zsh_prompt_$$
+
+ # Signal parent process
+ if (( AGKOZAK_PROMPT_DEBUG )); then
+ kill -s USR1 $$
+ else
+ kill -s USR1 $$ &> /dev/null
+ fi
+ }
+
+ ############################################################
+ # On SIGUSR1, fetch Git status from temprary file and store
+ # it in psvar[3]. This function caches its own code in
+ # AGKOZAK_TRAPUSR1_FUNCTION so that it can tell if it has
+ # been redefined by another script.
+ #
+ # Globals:
+ # AGKOZAK_USR1_ASYNC_WORKER
+ # AGKOZAK_TRAPUSR1_FUNCTION
+ ############################################################
+ TRAPUSR1() {
+ # Set prompt from contents of temporary file
+ psvar[3]=$(print -n -- "$(< /tmp/agkozak_zsh_prompt_$$)")
+
+ # Reset asynchronous process number
+ typeset -g AGKOZAK_USR1_ASYNC_WORKER=0
+
+ # Redraw the prompt
+ zle && zle reset-prompt
+ }
+
+ typeset -g AGKOZAK_TRAPUSR1_FUNCTION="$(builtin which TRAPUSR1)"
+ ;;
+ esac
+}
+
+######################################################################
+# THE PROMPT
+######################################################################
+
+############################################################
+# Strip color codes from a prompt string
+#
+# Arguments:
+# $1 The prompt string
+############################################################
+_agkozak_strip_colors() {
+
+ local prompt=$1
+ local open_braces
+
+ while [[ -n $prompt ]]; do
+ case $prompt in
+ %F\{*|%K\{*)
+ (( open_braces++ ))
+ prompt=${prompt#%[FK]\{}
+ while (( open_braces )); do
+ case ${prompt:0:1} in
+ \{) (( open_braces++ )) ;;
+ \}) (( open_braces-- )) ;;
+ esac
+ prompt=${prompt#?}
+ done
+ ;;
+ %f*|%k*) prompt=${prompt#%[fk]} ;;
+ *)
+ print -n -- "${prompt:0:1}"
+ prompt=${prompt#?}
+ ;;
+ esac
+ done
+}
+
+############################################################
+# Runs right before each prompt is displayed; hooks into
+# precmd
+#
+# 1) Redisplays path ($psvar[2]) whenever the value of
+# AGKOZAK_PROMPT_DIRTRIM or AGKOZAK_NAMED_DIRS changes
+# 2) If AGKOZAK_MULTILINE is changed to 0, set
+# AGKOZAK_LEFT_PROMPT_ONLY=0
+# 3) If AGKOZAK_LEFT_PROMPT_ONLY is changed, updated both
+# prompt strings
+# 4) Resets Git status and vi mode display
+# 5) Begins to calculate Git status
+# 6) Sets AGKOZAK_PROMPT_WHITESPACE based on value of
+# AGKOZAK_MULTILINE
+# 7) Optionally display a blank line (AGKOZAK_BLANK_LINES),
+# while avoiding a blank line when the shell is first
+# loaded
+# 8) If custom prompts are defined, update the prompt
+# strings
+#
+# TODO: Consider making AGKOZAK_PROMPT_WHITESPACE a psvar
+#
+# Globals:
+# AGKOZAK_PROMPT_DIRTRIM
+# AGKOZAK_OLD_PROMPT_DIRTRIM
+# AGKOZAK_NAMED_DIRS
+# AGKOZAK_OLD_NAMED_DIRS
+# AGKOZAK_MULTILINE
+# AGKOZAK_OLD_MULTILINE
+# AGKOZAK_LEFT_PROMPT_ONLY
+# AGKOZAK_OLD_LEFT_PROMPT_ONLY
+# AGKOZAK_ASYNC_METHOD
+# AGKOZAK_PROMPT_WHITESPACE
+# AGKOZAK_BLANK_LINES
+# AGKOZAK_FIRST_PROMPT_PRINTED
+# AGKOZAK_CUSTOM_PROMPT
+# AGKOZAK_CURRENT_CUSTOM_PROMPT
+# AGKOZAK_CUSTOM_RPROMPT
+# AGKOZAK_CURRENT_CUSTOM_RPROMPT
+############################################################
+_agkozak_precmd() {
+ # Update displayed directory when AGKOZAK_PROMPT_DIRTRIM or AGKOZAK_NAMED_DIRS
+ # changes or when first sourcing this script
+ if (( AGKOZAK_PROMPT_DIRTRIM != AGKOZAK_OLD_PROMPT_DIRTRIM )) \
+ || (( AGKOZAK_NAMED_DIRS != AGKOZAK_OLD_NAMED_DIRS )) \
+ || (( ! $+psvar[2] )); then
+ _agkozak_prompt_dirtrim -v $AGKOZAK_PROMPT_DIRTRIM
+ typeset -g AGKOZAK_OLD_PROMPT_DIRTRIM=$AGKOZAK_PROMPT_DIRTRIM
+ typeset -g AGKOZAK_OLD_NAMED_DIRS=$AGKOZAK_NAMED_DIRS
+ fi
+
+ if (( AGKOZAK_MULTILINE != AGKOZAK_OLD_MULTILINE )); then
+ (( AGKOZAK_MULTILINE == 0 )) && AGKOZAK_LEFT_PROMPT_ONLY=0
+ typeset -g AGKOZAK_OLD_MULTILINE=$AGKOZAK_MULTILINE
+ fi
+
+ if (( AGKOZAK_LEFT_PROMPT_ONLY != AGKOZAK_OLD_LEFT_PROMPT_ONLY )); then
+ unset AGKOZAK_CUSTOM_PROMPT AGKOZAK_CUSTOM_RPROMPT
+ typeset -g AGKOZAK_OLD_LEFT_PROMPT_ONLY=$AGKOZAK_LEFT_PROMPT_ONLY
+ _agkozak_prompt_string
+ fi
+
+ psvar[3]=''
+ psvar[4]=''
+
+ case $AGKOZAK_ASYNC_METHOD in
+ 'subst-async') _agkozak_subst_async ;;
+ 'zsh-async') _agkozak_zsh_async ;;
+ 'usr1') _agkozak_usr1_async ;;
+ *) psvar[3]="$(_agkozak_branch_status)" ;;
+ esac
+
+ if (( AGKOZAK_MULTILINE == 0 )) && (( ! AGKOZAK_LEFT_PROMPT_ONLY )) \
+ && [[ -z $INSIDE_EMACS ]]; then
+ typeset -g AGKOZAK_PROMPT_WHITESPACE=' '
+ else
+ typeset -g AGKOZAK_PROMPT_WHITESPACE=$'\n'
+ fi
+
+ if (( AGKOZAK_BLANK_LINES )); then
+ if (( AGKOZAK_FIRST_PROMPT_PRINTED )); then
+ print
+ fi
+ typeset -g AGKOZAK_FIRST_PROMPT_PRINTED=1
+ fi
+
+ # If AGKOZAK_CUSTOM_PROMPT or AGKOZAK_CUSTOM_RPROMPT changes, the
+ # corresponding prompt is updated
+
+ if [[ ${AGKOZAK_CUSTOM_PROMPT} != "${AGKOZAK_CURRENT_CUSTOM_PROMPT}" ]]; then
+ typeset -g AGKOZAK_CURRENT_CUSTOM_PROMPT=${AGKOZAK_CUSTOM_PROMPT}
+ PROMPT=${AGKOZAK_CUSTOM_PROMPT}
+ if ! _agkozak_has_colors; then
+ PROMPT=$(_agkozak_strip_colors "${PROMPT}")
+ fi
+ fi
+
+ if [[ ${AGKOZAK_CUSTOM_RPROMPT} != "${AGKOZAK_CURRENT_CUSTOM_RPROMPT}" ]]; then
+ typeset -g AGKOZAK_CURRENT_CUSTOM_RPROMPT=${AGKOZAK_CUSTOM_RPROMPT}
+ RPROMPT=${AGKOZAK_CUSTOM_RPROMPT}
+ if ! _agkozak_has_colors; then
+ RPROMPT=$(_agkozak_strip_colors "${RPROMPT}")
+ fi
+ fi
+}
+
+############################################################
+# Set the prompt strings
+#
+# Globals:
+# AGKOZAK_CUSTOM_PROMPT
+# AGKOZAK_COLORS_EXIT_STATUS
+# AGKOZAK_COLORS_USER_HOST
+# AGKOZAK_COLORS_PATH
+# AGKOZAK_PROMPT_WHITESPACE
+# AGKOZAK_COLORS_PROMPT_CHAR
+# AGKOZAK_PROMPT_CHAR
+# AGKOZAK_CURRENT_CUSTOM_PROMPT
+# AGKOZAK_CUSTOM_RPROMPT
+# AGKOZAK_COLORS_BRANCH_STATUS
+# AGKOZAK_CURRENT_CUSTOM_RPROMPT
+############################################################
+_agkozak_prompt_string () {
+ if (( $+AGKOZAK_CUSTOM_PROMPT )); then
+ PROMPT=${AGKOZAK_CUSTOM_PROMPT}
+ else
+ # The color left prompt
+ PROMPT='%(?..%B%F{${AGKOZAK_COLORS_EXIT_STATUS}}(%?%)%f%b )'
+ PROMPT+='%(!.%S%B.%B%F{${AGKOZAK_COLORS_USER_HOST}})%n%1v%(!.%b%s.%f%b) '
+ PROMPT+='%B%F{${AGKOZAK_COLORS_PATH}}%2v%f%b'
+ if (( AGKOZAK_LEFT_PROMPT_ONLY )); then
+ PROMPT+='%(3V.%F{${AGKOZAK_COLORS_BRANCH_STATUS}}%3v%f.)'
+ fi
+ PROMPT+='${AGKOZAK_PROMPT_WHITESPACE}'
+ PROMPT+='${AGKOZAK_COLORS_PROMPT_CHAR:+%F{${AGKOZAK_COLORS_PROMPT_CHAR}\}}'
+ PROMPT+='%(4V.${AGKOZAK_PROMPT_CHAR[3]:-:}.%(!.${AGKOZAK_PROMPT_CHAR[2]:-%#}.${AGKOZAK_PROMPT_CHAR[1]:-%#}))'
+ PROMPT+='${AGKOZAK_COLORS_PROMPT_CHAR:+%f} '
+
+ typeset -g AGKOZAK_CUSTOM_PROMPT=${PROMPT}
+ typeset -g AGKOZAK_CURRENT_CUSTOM_PROMPT=${AGKOZAK_CUSTOM_PROMPT}
+ fi
+
+ if (( $+AGKOZAK_CUSTOM_RPROMPT )); then
+ RPROMPT=${AGKOZAK_CUSTOM_RPROMPT}
+ else
+ # The color right prompt
+ if (( ! AGKOZAK_LEFT_PROMPT_ONLY )); then
+ typeset -g RPROMPT='%(3V.%F{${AGKOZAK_COLORS_BRANCH_STATUS}}%3v%f.)'
+ else
+ typeset -g RPROMPT=''
+ fi
+
+ typeset -g AGKOZAK_CUSTOM_RPROMPT=${RPROMPT}
+ typeset -g AGKOZAK_CURRENT_CUSTOM_RPROMPT=${RPROMPT}
+ fi
+
+ if ! _agkozak_has_colors; then
+ PROMPT=$(_agkozak_strip_colors "$PROMPT")
+ RPROMPT=$(_agkozak_strip_colors "$RPROMPT")
+ fi
+}
+
+############################################################
+# Prompt setup
+#
+# Globals:
+# AGKOZAK_ASYNC_METHOD
+# AGKOZAK_USR1_ASYNC_WORKER
+# AGKOZAK_PROMPT_DIRTRIM
+############################################################
+() {
+
+ _agkozak_async_init
+
+ case $AGKOZAK_ASYNC_METHOD in
+ 'subst-async') ;;
+ 'zsh-async') async_init ;;
+ 'usr1') typeset -g AGKOZAK_USR1_ASYNC_WORKER=0 ;;
+ esac
+
+ zle -N zle-keymap-select
+
+ # Don't use ZSH hooks in Emacs classic shell
+ if (( $+INSIDE_EMACS )) && [[ $TERM == 'dumb' ]]; then
+ :
+ else
+ autoload -Uz add-zsh-hook
+ add-zsh-hook precmd _agkozak_precmd
+
+ ############################################################
+ # Update the displayed directory when the PWD changes
+ ############################################################
+ _agkozak_chpwd() {
+ _agkozak_prompt_dirtrim -v $AGKOZAK_PROMPT_DIRTRIM
+ }
+
+ add-zsh-hook chpwd _agkozak_chpwd
+ fi
+
+ # Only display the HOSTNAME for an SSH connection or for a superuser
+ if _agkozak_is_ssh || (( EUID == 0 )); then
+ psvar[1]="@${HOST%%.*}"
+ else
+ psvar[1]=''
+ fi
+
+ # The DragonFly BSD console and Emacs shell can't handle bracketed paste.
+ # Avoid the ugly ^[[?2004 control sequence.
+ if [[ $TERM == 'cons25' ]] || [[ $TERM == 'dumb' ]]; then
+ unset zle_bracketed_paste
+ fi
+
+ # The Emacs shell has only limited support for some ZSH features, so use a
+ # more limited prompt.
+ if [[ $TERM == 'dumb' ]]; then
+ PROMPT='%(?..(%?%) )'
+ PROMPT+='%n%1v '
+ PROMPT+='$(_agkozak_prompt_dirtrim "$AGKOZAK_PROMPT_DIRTRIM")'
+ PROMPT+='$(_agkozak_branch_status) '
+ PROMPT+='%# '
+ else
+ # Avoid continuation lines in Emacs term and ansi-term
+ (( $+INSIDE_EMACS )) && ZLE_RPROMPT_INDENT=3
+
+ # When VSCode is using the DOM renderer, the right prompt overflows off the
+ # side of the screen
+ (( $+VSCODE_PID )) && ZLE_RPROMPT_INDENT=6
+
+ _agkozak_prompt_string
+
+ fi
+
+ _agkozak_debug_print "Using async method: $AGKOZAK_ASYNC_METHOD"
+}
+
+# Clean up environment
+unfunction _agkozak_load_async_lib _agkozak_has_usr1 _agkozak_is_ssh \
+ _agkozak_async_init
+
+# vim: ts=2:et:sts=2:sw=2:ROMPT='%~%<< $(git_prompt_info)${PR_BOLD_WHITE}>%{${reset_color}%} '
diff --git a/linux/home/.config/zsh/user/prompt_simple.zsh b/linux/home/.config/zsh/user/prompt_simple.zsh
new file mode 100644
index 0000000..0bbad44
--- /dev/null
+++ b/linux/home/.config/zsh/user/prompt_simple.zsh
@@ -0,0 +1,227 @@
+# vim:ft=zsh ts=2 sw=2 sts=2
+#
+### Segment drawing
+# A few utility functions to make it easy and re-usable to draw segmented prompts
+CURRENT_BG='NONE'
+
+case ${SOLARIZED_THEME:-dark} in
+ light) CURRENT_FG='white';;
+ *) CURRENT_FG='black';;
+esac
+
+# Segments
+() {
+ local LC_ALL="" LC_CTYPE="en_US.UTF-8"
+ SEGMENT_SEPARATOR=
+}
+
+# Begin a segment
+# Takes two arguments, background and foreground. Both can be omitted,
+# rendering default background/foreground.
+prompt_segment() {
+ local bg fg
+ [[ -n $2 ]] && fg="$FG[254]" || fg="%f"
+ if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then
+ echo -n " %{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%} "
+ else
+ echo -n "%{$bg%}%{$fg%}"
+ fi
+ CURRENT_BG=$1
+ [[ -n $3 ]] && echo -n $3
+}
+
+# End the prompt, closing any open segments
+prompt_end() {
+ if [[ -n $CURRENT_BG ]]; then
+ echo -n " %{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR"
+ else
+ echo -n "%{%k%}"
+ fi
+ echo -n "%{%f%}"
+ CURRENT_BG=''
+}
+
+### Prompt components
+# Each component will draw itself, and hide itself if no information needs to be shown
+
+# Context: user@hostname (who am I and where am I)
+prompt_context() { }
+
+parse_git_dirty() {
+ local -a git_status
+ git_status=($(git status --porcelain 2>/dev/null))
+ if [[ ${#git_status[@]} -gt 0 ]]; then
+ echo "±"
+ fi
+}
+
+# Git: branch/detached head, dirty status
+prompt_git() {
+ (( $+commands[git] )) || return
+ if [[ "$(git config --get oh-my-zsh.hide-status 2>/dev/null)" = 1 ]]; then
+ return
+ fi
+ local PL_BRANCH_CHAR
+ () {
+ local LC_ALL="" LC_CTYPE="en_US.UTF-8"
+ PL_BRANCH_CHAR=$'' # 
+ }
+ local ref dirty mode repo_path
+
+ if [[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]]; then
+ repo_path=$(git rev-parse --git-dir 2>/dev/null)
+ dirty=$(parse_git_dirty)
+ ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➦ $(git rev-parse --short HEAD 2> /dev/null)"
+ if [[ -n $dirty ]]; then
+ prompt_segment yellow black
+ PL_BRANCH_CHAR=%{%F{yellow}%}''
+ else
+ prompt_segment green $CURRENT_FG
+ fi
+
+ if [[ -e "${repo_path}/BISECT_LOG" ]]; then
+ mode=" <B>"
+ elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then
+ mode=" >M<"
+ elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then
+ mode=" >R>"
+ fi
+
+ setopt promptsubst
+ autoload -Uz vcs_info
+
+ zstyle ':vcs_info:*' enable git
+ zstyle ':vcs_info:*' get-revision true
+ zstyle ':vcs_info:*' check-for-changes true
+ zstyle ':vcs_info:*' stagedstr '✚'
+ zstyle ':vcs_info:*' unstagedstr '± '
+ zstyle ':vcs_info:*' formats ' %u%c'
+ zstyle ':vcs_info:*' actionformats ' %u%c'
+ vcs_info
+ echo -n "${ref/refs\/heads\//$PL_BRANCH_CHAR }${vcs_info_msg_0_%% }${mode}"
+ fi
+}
+
+prompt_bzr() {
+ (( $+commands[bzr] )) || return
+
+ # Test if bzr repository in directory hierarchy
+ local dir="$PWD"
+ while [[ ! -d "$dir/.bzr" ]]; do
+ [[ "$dir" = "/" ]] && return
+ dir="${dir:h}"
+ done
+
+ local bzr_status status_mod status_all revision
+ if bzr_status=$(bzr status 2>&1); then
+ status_mod=$(echo -n "$bzr_status" | head -n1 | grep "modified" | wc -m)
+ status_all=$(echo -n "$bzr_status" | head -n1 | wc -m)
+ revision=$(bzr log -r-1 --log-format line | cut -d: -f1)
+ if [[ $status_mod -gt 0 ]] ; then
+ prompt_segment yellow black "bzr@$revision ✚"
+ else
+ if [[ $status_all -gt 0 ]] ; then
+ prompt_segment yellow black "bzr@$revision"
+ else
+ prompt_segment green black "bzr@$revision"
+ fi
+ fi
+ fi
+}
+
+prompt_hg() {
+ (( $+commands[hg] )) || return
+ local rev st branch
+ if $(hg id >/dev/null 2>&1); then
+ if $(hg prompt >/dev/null 2>&1); then
+ if [[ $(hg prompt "{status|unknown}") = "?" ]]; then
+ # if files are not added
+ prompt_segment red white
+ st='±'
+ elif [[ -n $(hg prompt "{status|modified}") ]]; then
+ # if any modification
+ prompt_segment yellow black
+ st='±'
+ else
+ # if working copy is clean
+ prompt_segment green $CURRENT_FG
+ fi
+ echo -n $(hg prompt "☿ {rev}@{branch}") $st
+ else
+ st=""
+ rev=$(hg id -n 2>/dev/null | sed 's/[^-0-9]//g')
+ branch=$(hg id -b 2>/dev/null)
+ if `hg st | grep -q "^\?"`; then
+ prompt_segment red black
+ st='±'
+ elif `hg st | grep -q "^[MA]"`; then
+ prompt_segment yellow black
+ st='±'
+ else
+ prompt_segment green $CURRENT_FG
+ fi
+ echo -n "☿ $rev@$branch" $st
+ fi
+ fi
+}
+
+# Change prompt for HOME dir
+prompt_dir () {
+ if [[ "$PWD" == "$HOME" ]]; then
+ prompt_segment blue $CURRENT_FG ''
+ else
+ prompt_segment blue CURRENT_FG '%2~'
+ fi
+}
+
+# Virtualenv: current working virtualenv
+prompt_virtualenv() {
+ local virtualenv_path="$VIRTUAL_ENV"
+ if [[ -n $virtualenv_path && -n $VIRTUAL_ENV_DISABLE_PROMPT ]]; then
+ prompt_segment blue black "(`basename $virtualenv_path`)"
+ fi
+}
+
+# Status:
+# - was there an error
+# - am I root
+# - are there background jobs?
+prompt_status() {
+ local -a symbols
+
+ [[ $RETVAL -ne 0 ]] && symbols+=" %{%F{red}%}"
+ [[ $UID -eq 0 ]] && symbols+="%{%F{yellow}%}⚡"
+ [[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}⚙"
+
+ [[ -n "$symbols" ]] && prompt_segment black default "$symbols"
+}
+
+#AWS Profile:
+# - display current AWS_PROFILE name
+# - displays yellow on red if profile name contains 'production' or
+# ends in '-prod'
+# - displays black on green otherwise
+prompt_aws() {
+ [[ -z "$AWS_PROFILE" || "$SHOW_AWS_PROMPT" = false ]] && return
+ case "$AWS_PROFILE" in
+ *-prod|*production*) prompt_segment red yellow "AWS: $AWS_PROFILE" ;;
+ *) prompt_segment green black "AWS: $AWS_PROFILE" ;;
+ esac
+}
+
+## Main prompt
+build_prompt() {
+ RETVAL=$?
+ prompt_status
+ prompt_virtualenv
+ prompt_aws
+ prompt_context
+ prompt_dir
+ prompt_git
+ prompt_bzr
+ prompt_hg
+ prompt_end
+}
+
+PROMPT='%{%F{blue}%}  %{%f%b%k%}$(build_prompt) '
+bindkey -M vicmd '\e[C' vi-forward-char # ESC + right arrow