diff --git a/README.md b/README.md index 4f70b1f..c54b19e 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,4 @@ by invoking the `setup-new` script directly via `curl`: # Run curl -s https://raw.githubusercontent.com/andrejusk/dotfiles/HEAD/script/setup-new | bash + diff --git a/files/home/.aliases b/files/home/.aliases index 2e43598..e5e8d9e 100644 --- a/files/home/.aliases +++ b/files/home/.aliases @@ -1,6 +1,7 @@ alias bench='ZSH_BENCH=1 exec zsh' alias dots='cd $DOTFILES' alias j='z' +alias ji='zi' alias reload='source ~/.zshrc' alias reload-path='rm -f "${XDG_CACHE_HOME:-$HOME/.cache}/dots/path" && exec zsh' alias reload-cache='rm -rf "${XDG_CACHE_HOME:-$HOME/.cache}/dots" ~/.zcompdump* && exec zsh' diff --git a/files/home/.zshrc b/files/home/.zshrc index 1df9c49..b467c81 100644 --- a/files/home/.zshrc +++ b/files/home/.zshrc @@ -31,29 +31,89 @@ _dots_cache_ls_colors [[ -f ~/.aliases ]] && source ~/.aliases -_dots_load_omz() { - export DISABLE_AUTO_UPDATE="true" - export DISABLE_LS_COLORS="true" - export ZSH="$HOME/.oh-my-zsh" - export ZSH_THEME="" - plugins=( - z - zsh-autosuggestions - zsh-syntax-highlighting - ) - - # Daily security check: skip compaudit if already checked today - local marker="$_dots_cache_dir/.compaudit_checked" - local today=$(date +'%Y-%j') - if [[ -f "$marker" ]] && [[ "$(cat "$marker" 2>/dev/null)" == "$today" ]]; then - export ZSH_DISABLE_COMPFIX="true" +_dots_init_completion() { + # Add custom completions to fpath + local comp_dir="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/completions" + [[ -d "$comp_dir" ]] && fpath=("$comp_dir" $fpath) + + autoload -Uz compinit + # Rebuild zcompdump at most once per day + local dump="$HOME/.zcompdump" + if [[ -f "$dump" && $(date +'%j') == $(stat -f '%Sm' -t '%j' "$dump" 2>/dev/null || date -r "$dump" +'%j' 2>/dev/null) ]]; then + compinit -C else - echo "$today" > "$marker" + compinit fi - - source "$ZSH/oh-my-zsh.sh" } -_dots_load_omz +_dots_init_completion + +_dots_load_plugins() { + local plugin_dir="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins" + # autosuggestions first + local f="$plugin_dir/zsh-autosuggestions/zsh-autosuggestions.zsh" + [[ -f "$f" ]] && source "$f" + # syntax-highlighting must be last + f="$plugin_dir/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" + [[ -f "$f" ]] && source "$f" +} +_dots_load_plugins + +_dots_load_history() { + HISTFILE="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/history" + HISTSIZE=50000 + SAVEHIST=50000 + mkdir -p "$(dirname "$HISTFILE")" + setopt HIST_IGNORE_DUPS HIST_IGNORE_SPACE SHARE_HISTORY +} +_dots_load_history + +_dots_load_keybindings() { + bindkey -e + + bindkey '^[[H' beginning-of-line + bindkey '^[[F' end-of-line + + # Ctrl+J: interactive zoxide jump (zi) + _dots_zoxide_widget() { + local result + result="$(zoxide query -i -- 2>&1)" && cd "$result" + zle reset-prompt + } + zle -N _dots_zoxide_widget + bindkey '^J' _dots_zoxide_widget +} +_dots_load_keybindings + +_dots_load_fzf() { + command -v fzf &>/dev/null || return + export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git"' + export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND" + export FZF_DEFAULT_OPTS='--layout=reverse --height=40% --prompt="> " --info=inline-right --no-separator' + # Modern fzf (v0.48+) provides --zsh + if fzf --zsh &>/dev/null; then + source <(fzf --zsh) + else + # Fallback paths for shell integration + local -a fzf_paths=( + "${HOMEBREW_PREFIX:-/opt/homebrew}/opt/fzf/shell" + "/usr/share/fzf" + "${XDG_DATA_HOME:-$HOME/.local/share}/fzf/shell" + ) + for dir in "${fzf_paths[@]}"; do + [[ -f "$dir/key-bindings.zsh" ]] && source "$dir/key-bindings.zsh" && break + done + for dir in "${fzf_paths[@]}"; do + [[ -f "$dir/completion.zsh" ]] && source "$dir/completion.zsh" && break + done + fi +} + + +_dots_load_zoxide() { + command -v zoxide &>/dev/null || return + export _ZO_FZF_OPTS="$FZF_DEFAULT_OPTS" + eval "$(zoxide init zsh)" +} # Prompt (( ${+PROMPT_MIN_DURATION} )) || typeset -gi PROMPT_MIN_DURATION=2 # show duration after N seconds @@ -340,5 +400,7 @@ _dots_load_mise() { command -v mise &>/dev/null && eval "$(mise activate zsh)" } _dots_load_mise +_dots_load_fzf +_dots_load_zoxide [[ -n "$ZSH_BENCH" ]] && zprof || true diff --git a/script/install.d/22-zsh.sh b/script/install.d/22-zsh.sh index 569cd89..f34c3ca 100755 --- a/script/install.d/22-zsh.sh +++ b/script/install.d/22-zsh.sh @@ -2,7 +2,7 @@ # ----------------------------------------------------------------------------- # Description: -# Configure zsh shell. +# Configure zsh shell with direct plugin management. # # install zsh @@ -26,28 +26,33 @@ fi zsh --version -# install oh-my-zsh -export ZSH="$HOME/.oh-my-zsh" -if [ ! -d "$ZSH" ]; then - # https://github.com/ohmyzsh/ohmyzsh#unattended-install - bash -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended -fi -export ZSH_CUSTOM="$ZSH/custom" - -# install zsh-syntax-highlighting -export ZSH_SYNTAX_HIGHLIGHTING="$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" -if [ ! -d "$ZSH_SYNTAX_HIGHLIGHTING" ]; then - git clone -q \ - https://github.com/zsh-users/zsh-syntax-highlighting.git \ - "$ZSH_SYNTAX_HIGHLIGHTING" -fi +# plugin directory (XDG compliant) +PLUGIN_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins" +mkdir -p "$PLUGIN_DIR" # install zsh-autosuggestions -export ZSH_AUTOSUGGESTIONS="$ZSH_CUSTOM/plugins/zsh-autosuggestions" -if [ ! -d "$ZSH_AUTOSUGGESTIONS" ]; then +if [ ! -d "$PLUGIN_DIR/zsh-autosuggestions" ]; then git clone -q \ https://github.com/zsh-users/zsh-autosuggestions.git \ - "$ZSH_AUTOSUGGESTIONS" + "$PLUGIN_DIR/zsh-autosuggestions" +fi + +# install zsh-syntax-highlighting +if [ ! -d "$PLUGIN_DIR/zsh-syntax-highlighting" ]; then + git clone -q \ + https://github.com/zsh-users/zsh-syntax-highlighting.git \ + "$PLUGIN_DIR/zsh-syntax-highlighting" +fi + +# warn about legacy oh-my-zsh directory +if [ -d "$HOME/.oh-my-zsh" ]; then + log_warn "Legacy ~/.oh-my-zsh directory found. Remove with: rm -rf ~/.oh-my-zsh" +fi + +# migrate zoxide database from z if available +if command -v zoxide &>/dev/null && [ -f "$HOME/.z" ]; then + log_info "Importing z database into zoxide..." + zoxide import --from z "$HOME/.z" 2>/dev/null || true fi # change default shell to zsh diff --git a/script/install.d/30-mise.sh b/script/install.d/30-mise.sh index f81ed80..7859d12 100755 --- a/script/install.d/30-mise.sh +++ b/script/install.d/30-mise.sh @@ -48,6 +48,9 @@ typeset -a MISE_TOOLS=( "gh@2.86.0" "terraform@1.14.4" "firebase@15.5.1" + "fzf@latest" + "zoxide@latest" + "ripgrep@latest" ) # Install all tools in parallel @@ -64,14 +67,11 @@ done eval "$(mise activate bash)" export PATH="$HOME/.local/share/mise/shims:$PATH" -# Setup Poetry ZSH completions -ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}" -if [[ -d "$ZSH_CUSTOM/plugins" ]]; then - POETRY_PLUGIN="$ZSH_CUSTOM/plugins/poetry" - if [ ! -d "$POETRY_PLUGIN" ]; then - mkdir -p "$POETRY_PLUGIN" - mise exec -- poetry completions zsh > "$POETRY_PLUGIN/_poetry" - fi +# Setup Poetry ZSH completions (XDG compliant) +COMPLETIONS_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/completions" +mkdir -p "$COMPLETIONS_DIR" +if [ ! -f "$COMPLETIONS_DIR/_poetry" ]; then + mise exec -- poetry completions zsh > "$COMPLETIONS_DIR/_poetry" fi # Verify installations using mise exec diff --git a/tests/test_binaries.py b/tests/test_binaries.py index 2c9645d..11fb01b 100644 --- a/tests/test_binaries.py +++ b/tests/test_binaries.py @@ -65,6 +65,10 @@ binaries: List[Text] = [ # langauge: js "node", "npm", + # search & navigation + "fzf", + "zoxide", + "rg", ] binaries = [binary for binary in binaries if binary is not None]