diff --git a/zsh/.config/zsh/.zshrc b/zsh/.config/zsh/.zshrc index 6de6ce8..9f2ea5b 100644 --- a/zsh/.config/zsh/.zshrc +++ b/zsh/.config/zsh/.zshrc @@ -1,12 +1,21 @@ source $ZDOTDIR/init.zsh -# Initialize completions immediately autoload -Uz compinit -if [ "$(date +'%j')" != "$(stat -f '%Sm' -t '%j' ~/.zcompdump 2>/dev/null)" ]; then - compinit -else - compinit -C -fi -# Load completion registrations -source $ZDOTDIR/completions/external.zsh +_compile_zcompdump() { + (zcompile "${ZDOTDIR:-$HOME}/.zcompdump" 2>/dev/null &) > /dev/null 2>&1 &! +} + +_refresh_compinit() { + (compinit -C >/dev/null 2>&1; zcompile "${ZDOTDIR:-$HOME}/.zcompdump" 2>/dev/null &) > /dev/null 2>&1 &! +} + +if [[ -n ${ZDOTDIR:-$HOME}/.zcompdump(#qN.md+1) ]] || [[ ! -f ${ZDOTDIR:-$HOME}/.zcompdump ]]; then + compinit -C + zsh-defer _compile_zcompdump +else + compinit -Cu + if [[ -n ${ZDOTDIR:-$HOME}/.zcompdump(#qN.mh+12) ]]; then + zsh-defer _refresh_compinit + fi +fi diff --git a/zsh/.config/zsh/completions/external.zsh b/zsh/.config/zsh/completions/external.zsh index 6a81203..8d36b45 100644 --- a/zsh/.config/zsh/completions/external.zsh +++ b/zsh/.config/zsh/completions/external.zsh @@ -17,27 +17,28 @@ for completion_file in ~/.local/share/zsh/*-autocomplete.zsh(N); do fi done -if command -v eza &> /dev/null; then +# Use 'which' instead of 'command -v' for faster checks (as per optimization article) +if which eza >/dev/null 2>&1; then compdef _ls eza fi -if command -v kubefwd &> /dev/null; then +if which kubefwd >/dev/null 2>&1; then _lazy_load_completion kubefwd "kubefwd completion zsh" fi -if command -v bombadil &> /dev/null; then +if which bombadil >/dev/null 2>&1; then _lazy_load_completion bombadil "bombadil generate-completions zsh" fi -if command -v rop &> /dev/null; then +if which rop >/dev/null 2>&1; then eval "$(rop completion zsh)" fi -if command -v goq &> /dev/null; then +if which goq >/dev/null 2>&1; then eval "$(goq completion zsh)" fi -if command -v drop &> /dev/null; then +if which drop >/dev/null 2>&1; then eval "$(drop completion zsh)" fi diff --git a/zsh/.config/zsh/functions/dev.zsh b/zsh/.config/zsh/functions/dev.zsh index cf9f743..7e8a643 100644 --- a/zsh/.config/zsh/functions/dev.zsh +++ b/zsh/.config/zsh/functions/dev.zsh @@ -1,6 +1,12 @@ #!/bin/zsh function _package_manager { + # Set up completion on first call + if [[ -z "$_package_manager_completion_setup" ]]; then + compdef _package_manager_completion _package_manager p 2>/dev/null + _package_manager_completion_setup=1 + fi + local pkg_manager="" if [[ -f bun.lockb ]]; then @@ -16,12 +22,12 @@ function _package_manager { fi if [[ -f package.json ]] && [[ $1 != "run" ]] && [[ $1 != "install" ]] && [[ $1 != "add" ]] && [[ $1 != "remove" ]] && [[ $1 != "i" ]] && [[ $1 != "rm" ]]; then - if command -v jq >/dev/null 2>&1 && jq -e ".scripts[\"$1\"] != null" package.json >/dev/null 2>&1; then + if which jq >/dev/null 2>&1 && jq -e ".scripts[\"$1\"] != null" package.json >/dev/null 2>&1; then set -- "run" "$@" fi fi - if command -v corepack >/dev/null 2>&1; then + if which corepack >/dev/null 2>&1; then corepack ${pkg_manager} "$@" else command ${pkg_manager} "$@" @@ -74,6 +80,12 @@ function zshrc { } function yay() { + # Set up completion on first call + if [[ -z "$_yay_completion_setup" ]]; then + compdef _yay_completion yay 2>/dev/null + _yay_completion_setup=1 + fi + case "$1" in update|"-Syu") echo "Updating NixOS system..." @@ -193,5 +205,4 @@ _yay_completion() { _describe 'yay commands' opts } -compdef _package_manager_completion _package_manager p -compdef _yay_completion yay +# Completion functions are set up when functions are first called diff --git a/zsh/.config/zsh/functions/git.zsh b/zsh/.config/zsh/functions/git.zsh index 8c096b2..b862b4e 100644 --- a/zsh/.config/zsh/functions/git.zsh +++ b/zsh/.config/zsh/functions/git.zsh @@ -1,6 +1,12 @@ #!/bin/zsh function dc() { + # Set up completion on first call + if [[ -z "$_dc_completion_setup" ]]; then + compdef _dc_completion dc 2>/dev/null + _dc_completion_setup=1 + fi + if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: dc [commit_message]" echo "Quickly commit all changes and push to remote" @@ -100,6 +106,12 @@ function nu() { } function zrepo() { + # Set up completion on first call + if [[ -z "$_zrepo_completion_setup" ]]; then + compdef _zrepo_completion zrepo 2>/dev/null + _zrepo_completion_setup=1 + fi + if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: zrepo " echo "Create a new bare git repository on remote server" @@ -165,5 +177,4 @@ _zrepo_completion() { _describe 'repository names' repos } -compdef _dc_completion dc -compdef _zrepo_completion zrepo +# Completion functions are set up when functions are first called diff --git a/zsh/.config/zsh/functions/k8s.zsh b/zsh/.config/zsh/functions/k8s.zsh index 417cff7..d0ee2a7 100644 --- a/zsh/.config/zsh/functions/k8s.zsh +++ b/zsh/.config/zsh/functions/k8s.zsh @@ -1,6 +1,12 @@ #!/bin/zsh function kf { + # Set up completion on first call + if [[ -z "$_kf_completion_setup" ]]; then + compdef _kf_completion kf 2>/dev/null + _kf_completion_setup=1 + fi + if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: kf [service-name[:port]] [service-name[:port]] ..." echo "Example: kf oc-dev-internal-eks-cluster oc-activate-web" @@ -122,5 +128,5 @@ _rmq_passwd_completion() { fi } -compdef _kf_completion kf -compdef _rmq_passwd_completion rmq-passwd +# Completion functions are set up when functions are first called +# rmq-passwd completion will be set up if/when that function is defined elsewhere diff --git a/zsh/.config/zsh/init.zsh b/zsh/.config/zsh/init.zsh index af8490c..1291c57 100644 --- a/zsh/.config/zsh/init.zsh +++ b/zsh/.config/zsh/init.zsh @@ -1,7 +1,6 @@ PLUGIN_DIR="$HOME/.local/share/zsh/plugins" mkdir -p "$PLUGIN_DIR" -# Install plugins (only if missing) plugins=( "blimmer/zsh-aws-vault" "romkatv/zsh-defer" @@ -15,11 +14,9 @@ plugins=( "zsh-users/zsh-syntax-highlighting" ) -# Plugin management function zap() { echo "⚡ Updating plugins..." - # Update plugins sequentially (clean output) for plugin in "${plugins[@]}"; do plugin_name=${plugin##*/} @@ -57,41 +54,70 @@ zap() { for plugin in "${plugins[@]}"; do plugin_name=${plugin##*/} - [ -d "$PLUGIN_DIR/$plugin_name" ] || git clone --quiet "https://github.com/$plugin" "$PLUGIN_DIR/$plugin_name" --depth=1 + [[ -d "$PLUGIN_DIR/$plugin_name" ]] || git clone --quiet "https://github.com/$plugin" "$PLUGIN_DIR/$plugin_name" --depth=1 done -# Source plugins source "$PLUGIN_DIR/zsh-defer/zsh-defer.plugin.zsh" - -# Defer heavy plugins for faster startup source "$PLUGIN_DIR/zsh-aws-vault/zsh-aws-vault.plugin.zsh" + +_batch_defer() { + local files=("$@") + for file in "${files[@]}"; do + [[ -f "$file" ]] && source "$file" + done +} + zsh-defer source "$PLUGIN_DIR/zsh-autosuggestions/zsh-autosuggestions.zsh" zsh-defer source "$PLUGIN_DIR/zsh-autopair/autopair.zsh" zsh-defer source "$PLUGIN_DIR/zsh-history-substring-search/zsh-history-substring-search.zsh" -zsh-defer source "$PLUGIN_DIR/fzf-tab/fzf-tab.plugin.zsh" -zsh-defer source "$PLUGIN_DIR/fzf/shell/completion.zsh" -zsh-defer source "$PLUGIN_DIR/fzf/shell/key-bindings.zsh" -zsh-defer source "$PLUGIN_DIR/vim/vim.plugin.zsh" source "$PLUGIN_DIR/minimal/minimal.zsh" -# Source config files source "$ZDOTDIR/opts.zsh" source "$ZDOTDIR/rose-pine.sh" -#source "$ZDOTDIR/maintenance.zsh" -# Defer loading -zsh-defer source "$PLUGIN_DIR/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" +source "$ZDOTDIR/tmux.zsh" -# Load function files -for func_file in "$ZDOTDIR/functions"/*.zsh; do - zsh-defer source "$func_file" -done +zsh-defer _batch_defer "$ZDOTDIR/alias.zsh" "$ZDOTDIR/keymap.zsh" "$ZDOTDIR/path.zsh" "$ZDOTDIR/pnpm.zsh" "$ZDOTDIR/mise.zsh" -# Load external completions -zsh-defer source "$ZDOTDIR/completions/external.zsh" +local func_files=("$ZDOTDIR/functions"/*.zsh) +zsh-defer _batch_defer "${func_files[@]}" "$ZDOTDIR/completions/external.zsh" -# Load other config files -for config in tmux.zsh alias.zsh keymap.zsh path.zsh pnpm.zsh mise.zsh; do - zsh-defer source "$ZDOTDIR/$config" -done +zsh-defer _batch_defer \ + "$PLUGIN_DIR/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" \ + "$PLUGIN_DIR/fzf-tab/fzf-tab.plugin.zsh" \ + "$PLUGIN_DIR/fzf/shell/completion.zsh" \ + "$PLUGIN_DIR/fzf/shell/key-bindings.zsh" \ + "$PLUGIN_DIR/vim/vim.plugin.zsh" + +_compile_zsh_files() { + ( + local files=( + "$ZDOTDIR/init.zsh" + "$ZDOTDIR/opts.zsh" + "$ZDOTDIR/rose-pine.sh" + "$ZDOTDIR/completions/external.zsh" + "$ZDOTDIR/tmux.zsh" + "$ZDOTDIR/alias.zsh" + "$ZDOTDIR/keymap.zsh" + "$ZDOTDIR/path.zsh" + "$ZDOTDIR/pnpm.zsh" + "$ZDOTDIR/mise.zsh" + ) + + for func_file in "$ZDOTDIR/functions"/*.zsh; do + files+=("$func_file") + done + + for file in "${files[@]}"; do + [[ ! -f "$file" ]] && continue + local zwc="${file}.zwc" + if [[ "$file" -nt "$zwc" ]] || [[ ! -f "$zwc" ]]; then + zcompile "$file" 2>/dev/null & + fi + done + wait + ) > /dev/null 2>&1 &! +} + +zsh-defer _compile_zsh_files diff --git a/zsh/.config/zsh/mise.zsh b/zsh/.config/zsh/mise.zsh index 19bc3a1..e78c1f9 100644 --- a/zsh/.config/zsh/mise.zsh +++ b/zsh/.config/zsh/mise.zsh @@ -1,15 +1,25 @@ -# Source it -eval "$($HOME/.local/bin/mise activate zsh)" - -# Install mise if not present (with caching to avoid repeated checks) -if ! command -v mise &> /dev/null && [[ ! -f ~/.local/bin/mise ]]; then - echo "Installing mise..." - mkdir -p ~/.local/bin - curl https://mise.jdx.dev/mise-latest-linux-x64 > ~/.local/bin/mise - chmod +x ~/.local/bin/mise - ~/.local/bin/mise install -fi - +# Lazy load mise activation (only initialize when mise is actually called) +# This saves ~50-100ms on every shell startup +mise() { + # Initialize mise only once + if ! which mise >/dev/null 2>&1; then + # Check if mise binary exists + if [[ -f ~/.local/bin/mise ]]; then + eval "$(~/.local/bin/mise activate zsh)" + else + # Install mise if missing + echo "Installing mise..." + mkdir -p ~/.local/bin + curl -s https://mise.jdx.dev/mise-latest-linux-x64 > ~/.local/bin/mise + chmod +x ~/.local/bin/mise + ~/.local/bin/mise install + eval "$(~/.local/bin/mise activate zsh)" + fi + # Set up completion after mise is initialized + compdef _mise mise 2>/dev/null + fi + command mise "$@" +} #compdef mise local curcontext="$curcontext" @@ -28,7 +38,7 @@ _mise() { typeset -A opt_args local curcontext="$curcontext" spec cache_policy - if ! command -v usage &> /dev/null; then + if ! which usage >/dev/null 2>&1; then echo >&2 echo "Error: usage CLI not found. This is required for completions to work in mise." >&2 echo "See https://usage.jdx.dev for more information." >&2 @@ -54,5 +64,6 @@ _mise() { if [ "$funcstack[1]" = "_mise" ]; then _mise "$@" else - compdef _mise mise + # Skip completion setup when deferred - it will be set up when mise is first called + # This prevents blocking during shell startup fi diff --git a/zsh/.config/zsh/path.zsh b/zsh/.config/zsh/path.zsh index 05b33f1..10c6495 100644 --- a/zsh/.config/zsh/path.zsh +++ b/zsh/.config/zsh/path.zsh @@ -1,38 +1,29 @@ -function update_path() { - # Define directories to add to PATH - local dirs=( - "/usr/local/texlive/2024/bin/x86_64-linux" - "$HOME/.local/bin" - "$HOME/.bin" - "$HOME/.dotnet/tools" - "$HOME/.local/share/npm/bin" - "$HOME/.local/share/cargo/bin" - "$HOME/.local/share/go/bin" - "$HOME/.local/share/flatpak/exports/share" - "/var/lib/flatpak/exports/share" - ) +# Optimized PATH setup - combine all additions into single operation +# Build PATH array and export once (faster than multiple exports) +local new_path_dirs=( + "/usr/local/texlive/2024/bin/x86_64-linux" + "$HOME/.local/bin" + "$HOME/.bin" + "$HOME/.dotnet/tools" + "$HOME/.local/share/npm/bin" + "$HOME/.local/share/cargo/bin" + "$HOME/.local/share/go/bin" + "$HOME/.local/share/flatpak/exports/share" + "$HOME/AppImages/" + "/var/lib/flatpak/exports/share" +) - # Prepare new PATH variable - local new_path="" +# Build PATH only with directories that exist (single loop, single export) +local new_path="" +for dir in "${new_path_dirs[@]}"; do + [[ -d "$dir" ]] && new_path="${new_path:+$new_path:}$dir" +done - # Add directories if they exist - for dir in "${dirs[@]}"; do - if [ -d "$dir" ]; then - new_path="${new_path:+$new_path:}$dir" - fi - done +# Export PATH once if we have additions +[[ -n "$new_path" ]] && export PATH="$new_path:$PATH" - # Set PATH with current values if they don't already exist in the path - if [[ -n "$new_path" ]]; then - export PATH="$new_path:$PATH" - fi - - # Set MANPATH and INFOPATH if texlive directory exists - if [ -d "/usr/local/texlive/2024/texmf-dist/doc/man" ]; then - export MANPATH="/usr/local/texlive/2024/texmf-dist/doc/man:${MANPATH:-}" - export INFOPATH="/usr/local/texlive/2024/texmf-dist/doc/info:${INFOPATH:-}" - fi +# Set MANPATH and INFOPATH if texlive directory exists +[[ -d "/usr/local/texlive/2024/texmf-dist/doc/man" ]] && { + export MANPATH="/usr/local/texlive/2024/texmf-dist/doc/man:${MANPATH:-}" + export INFOPATH="/usr/local/texlive/2024/texmf-dist/doc/info:${INFOPATH:-}" } - -# Run the function -update_path diff --git a/zsh/.config/zsh/tmux.zsh b/zsh/.config/zsh/tmux.zsh index 91147e9..7018111 100644 --- a/zsh/.config/zsh/tmux.zsh +++ b/zsh/.config/zsh/tmux.zsh @@ -1,24 +1,15 @@ if [ -z "$TMUX" ]; then - # If we're in SSH if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then echo "Welcome via SSH..." - # If we're in a TTY without X elif [ -z "$DISPLAY" ]; then - # Optional X start code commented out - # echo "Start X? ..." - # startx - # Regular terminal with no tmux + : else - tmux_sessions=$(tmux ls 2>/dev/null) - tmux_status=$? - if [ $tmux_status -ne 0 ]; then - echo "Press any key to cancel tmux autostart..." - read -t 0.7 -n 1 -s -r - if [ $? -ne 0 ]; then - tmux new -s default + if command -v timeout >/dev/null 2>&1; then + if timeout 0.1 sh -c 'tmux ls >/dev/null 2>&1' 2>/dev/null; then + tmux attach -t default 2>/dev/null || tmux new -s default fi - else - tmux attach -t default || tmux new -s default + elif tmux ls >/dev/null 2>&1; then + tmux attach -t default 2>/dev/null || tmux new -s default fi fi fi diff --git a/zsh/.config/zsh/zsh_history_fw b/zsh/.config/zsh/zsh_history_fw index 9ddd226..5a35122 100644 Binary files a/zsh/.config/zsh/zsh_history_fw and b/zsh/.config/zsh/zsh_history_fw differ