- set bindkey -e before custom binds and load zsh/terminfo early - remove later bindkey -e that reset earlier keymaps - preserve terminfo application mode hooks This prevents Ctrl+Arrow and other navigation keys from failing until .zshrc is re-sourced. 🤖 Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode <noreply@opencode.ai>
314 lines
10 KiB
Bash
314 lines
10 KiB
Bash
# =====================
|
|
# Dotfiles Zshrc: conventions, device profiles, plugins, workflow
|
|
# =====================
|
|
# - Modular, idempotent, and device-aware (laptop/desktop via $DOTFILES_DEVICE)
|
|
# - All plugins managed via Zinit for speed and maintainability
|
|
# - Developer/power user aliases, functions, and keybinds
|
|
# - Device-specific fragments: ~/.zshrc.laptop, ~/.zshrc.desktop (auto-sourced)
|
|
# - Commit after each logical change with descriptive message
|
|
# - See AGENTS.md for full conventions
|
|
|
|
# =====================
|
|
# Environment settings
|
|
# =====================
|
|
|
|
# Ensure ~/.local/bin is in PATH (XDG standard)
|
|
if [ -d "$HOME/.local/bin" ] && [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
fi
|
|
# Optionally add XDG-compliant bin dir if present
|
|
if [ -d "${XDG_DATA_HOME:-$HOME/.local/share}/bin" ] && [[ ":$PATH:" != *":${XDG_DATA_HOME:-$HOME/.local/share}/bin:"* ]]; then
|
|
export PATH="${XDG_DATA_HOME:-$HOME/.local/share}/bin:$PATH"
|
|
fi
|
|
|
|
# Do Not Track for CLI tools
|
|
export DO_NOT_TRACK=1
|
|
|
|
# Preferred editor
|
|
export EDITOR='nvim'
|
|
|
|
# =====================
|
|
# Device profile sourcing (laptop/desktop)
|
|
# =====================
|
|
if [[ -n "$DOTFILES_DEVICE" ]]; then
|
|
if [[ "$DOTFILES_DEVICE" == "laptop" && -f "$HOME/.zshrc.laptop" ]]; then
|
|
source "$HOME/.zshrc.laptop"
|
|
elif [[ "$DOTFILES_DEVICE" == "desktop" && -f "$HOME/.zshrc.desktop" ]]; then
|
|
source "$HOME/.zshrc.desktop"
|
|
fi
|
|
fi
|
|
|
|
# =====================
|
|
# Zinit plugin manager setup
|
|
# =====================
|
|
ZINIT_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/zinit/zinit.git"
|
|
if [[ ! -d $ZINIT_HOME ]]; then
|
|
mkdir -p "$(dirname $ZINIT_HOME)"
|
|
git clone https://github.com/zdharma-continuum/zinit.git "$ZINIT_HOME"
|
|
fi
|
|
source "$ZINIT_HOME/zinit.zsh"
|
|
|
|
# =====================
|
|
# Zinit auto-update logic (updates Zinit and plugins once per week)
|
|
# =====================
|
|
ZINIT_UPDATE_STAMP="$HOME/.cache/zinit-last-update"
|
|
ZINIT_UPDATE_INTERVAL=$((7*24*60*60)) # 7 days (weekly) in seconds
|
|
zinit_auto_update() {
|
|
local now=$(date +%s)
|
|
local last=0
|
|
if [[ -f "$ZINIT_UPDATE_STAMP" ]]; then
|
|
last=$(cat "$ZINIT_UPDATE_STAMP" 2>/dev/null)
|
|
fi
|
|
if (( now - last > ZINIT_UPDATE_INTERVAL )); then
|
|
echo "[zinit] Auto-updating Zinit and plugins..."
|
|
zinit self-update && zinit update --all
|
|
echo $now > "$ZINIT_UPDATE_STAMP"
|
|
fi
|
|
}
|
|
# Run auto-update on shell startup (fast if already up-to-date)
|
|
zinit_auto_update
|
|
# Manual update alias
|
|
alias zinit-update-now='zinit self-update && zinit update --all && date +%s > "$ZINIT_UPDATE_STAMP" && echo "[zinit] Manual update complete."'
|
|
|
|
# =====================
|
|
# Prompt and UI
|
|
# =====================
|
|
|
|
# --- Zinit plugin loading ---
|
|
# All plugins are loaded via Zinit for modularity and speed
|
|
zinit light zsh-users/zsh-autosuggestions
|
|
zinit light zsh-users/zsh-syntax-highlighting
|
|
zinit light Aloxaf/fzf-tab
|
|
zinit light zsh-users/zsh-completions
|
|
zinit light zsh-users/zsh-history-substring-search
|
|
zinit snippet OMZP::git
|
|
# Starship prompt initialization
|
|
export STARSHIP_CONFIG="$HOME/.config/starship.toml"
|
|
if command -v starship >/dev/null; then
|
|
eval "$(starship init zsh)"
|
|
fi
|
|
|
|
# --- Starship prompt ---
|
|
# Starship prompt is now used exclusively. See https://starship.rs for config.
|
|
# Device profile info can be added via Starship config if needed.
|
|
|
|
|
|
# =====================
|
|
# fzf integration
|
|
# =====================
|
|
|
|
# fzf key bindings and completion
|
|
if [ -f /usr/share/fzf/key-bindings.zsh ]; then
|
|
source /usr/share/fzf/key-bindings.zsh
|
|
fi
|
|
if [ -f /usr/share/fzf/completion.zsh ]; then
|
|
source /usr/share/fzf/completion.zsh
|
|
fi
|
|
|
|
|
|
# =====================
|
|
# Aliases & Functions (Developer Workflow)
|
|
# =====================
|
|
|
|
# Modern replacements for coreutils
|
|
alias cat="bat"
|
|
alias ls="exa --icons"
|
|
alias ll="exa -l --icons"
|
|
alias la="exa -la --icons"
|
|
|
|
# Developer tools
|
|
alias lg="lazygit"
|
|
alias gs="git status"
|
|
alias ga="git add"
|
|
alias gc="git commit"
|
|
alias gco="git checkout"
|
|
alias gcm="git commit -m"
|
|
alias glog="git log --oneline --graph --decorate"
|
|
alias gpull="git pull"
|
|
alias gpush="git push"
|
|
alias gdiff="git diff"
|
|
alias gbr="git branch"
|
|
alias gsw="git switch"
|
|
alias gcl="git clone"
|
|
|
|
# Navigation
|
|
alias ..="cd .."
|
|
alias ...="cd ../.."
|
|
|
|
# =====================
|
|
# Robust keybinds for navigation and editing (support most terminals)
|
|
# =====================
|
|
|
|
# Ensure Emacs keymap is set before custom bindings so later changes don't reset them
|
|
bindkey -e
|
|
|
|
# Load terminfo module for $terminfo[] and echoti
|
|
zmodload zsh/terminfo 2>/dev/null || true
|
|
|
|
# VSCode terminal compatibility detection
|
|
export IS_VSCODE_TERM=0
|
|
if [[ "$TERM_PROGRAM" == "vscode" ]] || [[ -n "$VSCODE_IPC_HOOK_CLI" ]]; then
|
|
export IS_VSCODE_TERM=1
|
|
fi
|
|
|
|
if (( IS_VSCODE_TERM )); then
|
|
# VSCode terminal: use standard escape sequences for keybinds
|
|
bindkey '^[[H' beginning-of-line
|
|
bindkey '^[[F' end-of-line
|
|
bindkey '^[[2~' overwrite-mode
|
|
bindkey '^[[3~' delete-char
|
|
bindkey '^[[5~' beginning-of-buffer-or-history
|
|
bindkey '^[[6~' end-of-buffer-or-history
|
|
bindkey '^[[A' up-line-or-history
|
|
bindkey '^[[B' down-line-or-history
|
|
bindkey '^[[C' forward-char
|
|
bindkey '^[[D' backward-char
|
|
bindkey '^[[1;5D' backward-word # Ctrl+Left
|
|
bindkey '^[[1;5C' forward-word # Ctrl+Right
|
|
bindkey '^[[1;3D' backward-word # Alt+Left
|
|
bindkey '^[[1;3C' forward-word # Alt+Right
|
|
else
|
|
# Use terminfo for portability
|
|
typeset -g -A key
|
|
key[Home]="${terminfo[khome]}"
|
|
key[End]="${terminfo[kend]}"
|
|
key[Insert]="${terminfo[kich1]}"
|
|
key[Delete]="${terminfo[kdch1]}"
|
|
key[Up]="${terminfo[kcuu1]}"
|
|
key[Down]="${terminfo[kcud1]}"
|
|
key[Left]="${terminfo[kcub1]}"
|
|
key[Right]="${terminfo[kcuf1]}"
|
|
key[PageUp]="${terminfo[kpp]}"
|
|
key[PageDown]="${terminfo[knp]}"
|
|
key[Shift-Tab]="${terminfo[kcbt]}"
|
|
|
|
[[ -n "${key[Home]}" ]] && bindkey -- "${key[Home]}" beginning-of-line
|
|
[[ -n "${key[End]}" ]] && bindkey -- "${key[End]}" end-of-line
|
|
[[ -n "${key[Insert]}" ]] && bindkey -- "${key[Insert]}" overwrite-mode
|
|
[[ -n "${key[Delete]}" ]] && bindkey -- "${key[Delete]}" delete-char
|
|
[[ -n "${key[Up]}" ]] && bindkey -- "${key[Up]}" up-line-or-history
|
|
[[ -n "${key[Down]}" ]] && bindkey -- "${key[Down]}" down-line-or-history
|
|
[[ -n "${key[Left]}" ]] && bindkey -- "${key[Left]}" backward-char
|
|
[[ -n "${key[Right]}" ]] && bindkey -- "${key[Right]}" forward-char
|
|
[[ -n "${key[PageUp]}" ]] && bindkey -- "${key[PageUp]}" beginning-of-buffer-or-history
|
|
[[ -n "${key[PageDown]}" ]] && bindkey -- "${key[PageDown]}" end-of-buffer-or-history
|
|
[[ -n "${key[Shift-Tab]}" ]] && bindkey -- "${key[Shift-Tab]}" reverse-menu-complete
|
|
|
|
# Common escape sequences for Ctrl/Alt + Arrow keys (Alacritty, xterm, etc.)
|
|
bindkey '^[[1;5D' backward-word # Ctrl+Left
|
|
bindkey '^[[1;5C' forward-word # Ctrl+Right
|
|
bindkey '^[Od' backward-word # Ctrl+Left (alternate)
|
|
bindkey '^[Oc' forward-word # Ctrl+Right (alternate)
|
|
bindkey '^[[1;3D' backward-word # Alt+Left
|
|
bindkey '^[[1;3C' forward-word # Alt+Right
|
|
fi
|
|
|
|
# Already present: Alt+b/f for word movement
|
|
# bindkey '^[b' backward-word
|
|
# bindkey '^[f' forward-word
|
|
|
|
# Optionally, Alt+Up/Down for directory navigation (custom widgets can be added)
|
|
|
|
# Ensure terminal is in application mode for terminfo keycodes
|
|
if (( ${+terminfo[smkx]} && ${+terminfo[rmkx]} )); then
|
|
autoload -Uz add-zle-hook-widget
|
|
function zle_application_mode_start { echoti smkx }
|
|
function zle_application_mode_stop { echoti rmkx }
|
|
add-zle-hook-widget -Uz zle-line-init zle_application_mode_start
|
|
add-zle-hook-widget -Uz zle-line-finish zle_application_mode_stop
|
|
fi
|
|
|
|
# Quick edit .zshrc
|
|
alias ezrc="nvim ~/.zshrc"
|
|
|
|
# Function: reload zsh config
|
|
reload-zsh() { source ~/.zshrc && echo 'Reloaded .zshrc'; }
|
|
|
|
|
|
# =====================
|
|
# Zsh history settings
|
|
# =====================
|
|
|
|
HISTFILE=~/.zsh_history
|
|
HISTSIZE=10000
|
|
SAVEHIST=10000
|
|
setopt append_history
|
|
setopt inc_append_history
|
|
setopt share_history
|
|
setopt hist_ignore_dups
|
|
setopt hist_reduce_blanks
|
|
|
|
|
|
# =====================
|
|
# Zsh plugin integrations
|
|
# =====================
|
|
|
|
# Autosuggestions
|
|
if [ -f /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh ]; then
|
|
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
|
|
# Accept autosuggestion with Ctrl+F or Right Arrow
|
|
bindkey '^F' autosuggest-accept
|
|
bindkey '^[[C' autosuggest-accept
|
|
fi
|
|
# Syntax highlighting
|
|
if [ -f /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ]; then
|
|
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
|
|
fi
|
|
# Additional completions
|
|
fpath+=/usr/share/zsh/site-functions
|
|
autoload -Uz compinit && compinit
|
|
|
|
# =====================
|
|
# Keybinds (Navigation, Editing, FZF, Completion)
|
|
# =====================
|
|
|
|
# Emacs/vi mode selection handled earlier to avoid resetting custom binds
|
|
# bindkey -v # Uncomment to use vi mode instead
|
|
|
|
# History search
|
|
bindkey '^R' fzf-history-widget # fzf history search (fzf-tab)
|
|
bindkey '^S' history-incremental-search-forward
|
|
bindkey '^H' history-substring-search-backward # substring search (plugin)
|
|
bindkey '^P' history-substring-search-forward # substring search (plugin)
|
|
|
|
# Navigation
|
|
bindkey '^A' beginning-of-line
|
|
bindkey '^E' end-of-line
|
|
bindkey '^[b' backward-word
|
|
bindkey '^[f' forward-word
|
|
|
|
# Editing
|
|
bindkey '^U' kill-whole-line
|
|
bindkey '^W' backward-kill-word
|
|
bindkey '^K' kill-line
|
|
bindkey '^Y' yank
|
|
|
|
# Completion
|
|
bindkey '^I' expand-or-complete
|
|
bindkey '^ ' menu-complete
|
|
|
|
# fzf keybinds (if fzf is loaded)
|
|
if [ -n "$FZF_TMUX" ] || [ -f /usr/share/fzf/key-bindings.zsh ]; then
|
|
# Ctrl+T: fzf file search
|
|
bindkey '^T' fzf-file-widget
|
|
# Alt+C: fzf cd widget
|
|
bindkey '^[c' fzf-cd-widget
|
|
fi
|
|
|
|
# FZF-tab completion (tab for fuzzy completion)
|
|
# fzf-tab plugin handles completion menu automatically
|
|
|
|
# Documented: All keybinds above are for fast navigation, editing, history, and fuzzy search
|
|
|
|
# =====================
|
|
# Command-not-found handler (Arch/pkgfile)
|
|
# =====================
|
|
|
|
if [ -f /usr/share/doc/pkgfile/command-not-found.zsh ]; then
|
|
source /usr/share/doc/pkgfile/command-not-found.zsh
|
|
fi
|
|
|
|
# =====================
|
|
# End of .zshrc
|
|
# =====================
|