From c9ab492c6058f30334a6f88a797002fbf1937637 Mon Sep 17 00:00:00 2001 From: sebastian Date: Mon, 14 Sep 2026 15:14:05 +0200 Subject: [PATCH] cleanup of zshrc --- dot_zshrc | 400 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 320 insertions(+), 80 deletions(-) diff --git a/dot_zshrc b/dot_zshrc index f6dcac7..7bf165b 100644 --- a/dot_zshrc +++ b/dot_zshrc @@ -1,127 +1,367 @@ -# Set up the prompt - -# 2025-04-10 test +# ============================================================================= +# ~/.zshrc +# ============================================================================= +# Reihenfolge ist hier Absicht: +# 1. Profiling-Schalter +# 2. tmux-exec (ganz oben, damit die aeussere Shell sich die Init spart) +# 3. billige Optionen / Keybindings / History +# 4. teure Sachen: compinit + gecachte eval-Ausgaben +# 5. Aliases, Funktionen, PATH # +# Cache leeren nach Theme-/Tool-Update: zshrc-refresh +# Startzeit messen: ZSHRC_PROFILE=1 zsh -i -c exit +# ============================================================================= -autoload -Uz promptinit -promptinit -prompt adam1 -setopt histignorealldups sharehistory +# ----------------------------------------------------------------------------- +# 1. Profiling (optional, per Env-Variable) +# ----------------------------------------------------------------------------- +if [[ -n "$ZSHRC_PROFILE" ]]; then + zmodload zsh/zprof +fi -# Use emacs keybindings even if our EDITOR is set to vi -bindkey -e -# Keep 1000 lines of history within the shell and save it to ~/.zsh_history: -HISTSIZE=1000 -SAVEHIST=1000 +# ----------------------------------------------------------------------------- +# 2. tmux-Autostart +# ----------------------------------------------------------------------------- +# Muss vor allem anderen stehen: die aeussere Shell wird durch tmux ersetzt und +# muss deshalb gar nicht erst komplett initialisiert werden. Frueher lief das +# zshrc pro Terminalstart zweimal komplett durch (einmal aussen, einmal im +# tmux-Fenster) - das faellt damit weg. +# +# Terminalerkennung laeuft ueber Environment-Variablen statt ueber ps/proc, +# das spart ~6 Forks pro Shellstart. +if [[ -o interactive && -z "$TMUX" && -z "$ZSHRC_PROFILE" && $UID -ne 0 && -t 1 ]]; then + # IDEs und eingebettete Terminals explizit ausnehmen + if [[ -z "$TERMINAL_EMULATOR" && -z "$INSIDE_EMACS" \ + && -z "$VSCODE_INJECTION" && -z "$NVIM" ]]; then + + _zsh_want_tmux=0 + [[ "$TERM_PROGRAM" == ghostty || -n "$GHOSTTY_RESOURCES_DIR" ]] && _zsh_want_tmux=1 + [[ -n "$KITTY_WINDOW_ID" ]] && _zsh_want_tmux=1 + [[ -n "$VTE_VERSION" ]] && _zsh_want_tmux=1 # xfce4-terminal, gnome-terminal + [[ -n "$ZSH_FORCE_TMUX" ]] && _zsh_want_tmux=1 # manueller Override + [[ -n "$ZSH_NO_TMUX" ]] && _zsh_want_tmux=0 + + if (( _zsh_want_tmux )) && (( $+commands[tmux] )); then + unset _zsh_want_tmux + exec tmux new-session -A -s main + fi + unset _zsh_want_tmux + fi +fi + + +# ----------------------------------------------------------------------------- +# 3. Grundoptionen, Keybindings, History +# ----------------------------------------------------------------------------- +bindkey -e # Emacs-Keybindings, auch wenn EDITOR=vi +setopt interactivecomments # "#" auf der Kommandozeile = Kommentar + HISTFILE=~/.zsh_history +HISTSIZE=50000 # Eintraege im RAM +SAVEHIST=50000 # Eintraege in der Datei + # (vorher stand SAVEHIST auf 1000 - HISTFILESIZE + # ist eine Bash-Variable und hat nichts getan) -HISTSIZE=10000 -HISTFILESIZE=50000 -HISTTIMEFORMAT="[%Y-%m-%d %H:%m] " -HISTIGNORE="?:??:??:bash:zsh:clear:exit" +setopt hist_ignore_all_dups # aeltere Duplikate rauswerfen +setopt hist_ignore_space # Zeilen mit fuehrendem Leerzeichen nicht merken +setopt hist_reduce_blanks +setopt hist_verify # Expansion erst anzeigen, dann ausfuehren +setopt extended_history # mit Zeitstempel (Ersatz fuer HISTTIMEFORMAT) +setopt share_history # History zwischen laufenden Shells teilen -PROMPT_COMMAND="history -a; history -n" +# Falls die Shell sich beim Start traege anfuehlt, obwohl sonst alles schnell +# ist: share_history liest bei jedem Prompt die History-Datei neu ein. Dann +# stattdessen mal testen: +# unsetopt share_history +# setopt inc_append_history -# Use interactive Comments, so typing "#" in the prompt will be considered a comment -setopt interactivecomments -setopt share_history -# Use modern completion system -autoload -Uz compinit -compinit +# ----------------------------------------------------------------------------- +# 4a. Cache-Verzeichnis +# ----------------------------------------------------------------------------- +ZSH_CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/zsh +[[ -d $ZSH_CACHE_DIR ]] || mkdir -p $ZSH_CACHE_DIR + +# Pfad zum oh-my-posh-Theme +OMP_CONFIG=$HOME/.zsh/rudolfs-dark.omp.json + + +# ----------------------------------------------------------------------------- +# 4b. Completion-System +# ----------------------------------------------------------------------------- +# Nacktes "compinit" laeuft bei jedem Start compaudit und statet alle +# $fpath-Verzeichnisse ab - hunderte stat()-Calls. Bei warmem Page-Cache egal, +# bei kaltem Cache (also genau nach Idle) der teuerste Einzelposten. +# Deshalb: voller Audit nur einmal pro Tag, sonst direkt aus dem Dump. +() { + emulate -L zsh + setopt local_options extended_glob + + local zdump=$ZSH_CACHE_DIR/zcompdump + + autoload -Uz compinit + if [[ -n $zdump(#qNmh-24) ]]; then + compinit -C -d $zdump # Dump juenger als 24h -> Check ueberspringen + else + compinit -d $zdump # einmal taeglich voll durchlaufen + touch $zdump + fi + + # Dump kompilieren: laedt deutlich schneller, gerade von kalter Platte + if [[ ! -s ${zdump}.zwc || $zdump -nt ${zdump}.zwc ]]; then + zcompile $zdump 2>/dev/null + fi +} zstyle ':completion:*' auto-description 'specify: %d' zstyle ':completion:*' completer _expand _complete _correct _approximate zstyle ':completion:*' format 'Completing %d' zstyle ':completion:*' group-name '' -zstyle ':completion:*' menu select=2 -eval "$(dircolors -b)" -zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS} -zstyle ':completion:*' list-colors '' +zstyle ':completion:*' menu select=long zstyle ':completion:*' list-prompt %SAt %p: Hit TAB for more, or the character to insert%s zstyle ':completion:*' matcher-list '' 'm:{a-z}={A-Z}' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=* l:|=*' -zstyle ':completion:*' menu select=long zstyle ':completion:*' select-prompt %SScrolling active: current selection at %p%s zstyle ':completion:*' use-compctl false zstyle ':completion:*' verbose true +# Completion-Ergebnisse cachen (z.B. apt-Paketlisten) +zstyle ':completion:*' use-cache on +zstyle ':completion:*' cache-path $ZSH_CACHE_DIR/compcache + +# list-colors: die Zeile "zstyle ':completion:*' list-colors ''" im alten zshrc +# hat die LS_COLORS-Zuweisung direkt darueber wieder plattgemacht - deshalb hier +# nur noch einmal, weiter unten nach dircolors. + zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31' zstyle ':completion:*:kill:*' command 'ps -u $USER -o pid,%cpu,tty,cputime,cmd' - -## Oh my posh -# https://ohmyposh.dev/docs/installation/prompt -eval "$(oh-my-posh init zsh --config /home/sebastian/.zsh/rudolfs-dark.omp.json)" +# SSH-Hosts: mit "zstyle -e" wird das erst beim Tab-Druck ausgewertet, +# nicht mehr bei jedem Shellstart. +zstyle -e ':completion:*:*:ssh:*' hosts \ + 'reply=(${(f)"$(grep -h "^Host " ~/.ssh/config ~/.ssh/config.d/*(N) 2>/dev/null | sed "s/^Host //" | tr " " "\n" | grep -v "[*?]")"})' +# ----------------------------------------------------------------------------- +# 4c. Gecachte Init-Ausgaben (television, dircolors) +# ----------------------------------------------------------------------------- +# Beide Tools geben bei jedem Start denselben statischen Shell-Code aus. Statt +# sie jedes Mal zu starten, wird die Ausgabe einmal in eine Datei geschrieben, +# kompiliert und danach nur noch gesourct. # -# Fuzzy File Finder -# +# WICHTIG: die Bloecke brauchen eine Trennzeile. Manche Tools beenden ihre +# Ausgabe ohne Newline, dann klebt die erste Zeile des naechsten Blocks hinten +# an der letzten Zeile des vorigen - und man bekommt kaputte Pfade wie +# ".../init.26.0.5.abc..zsh#compdef". +() { + emulate -L zsh + local cache=$ZSH_CACHE_DIR/init.zsh + local stale=0 -#export FZF_TMUX_OPTS="-r 30%" -#export FZF_TMUX=1 -#source /usr/share/doc/fzf/examples/key-bindings.zsh + [[ -s $cache ]] || stale=1 + [[ -f ~/.dircolors && ~/.dircolors -nt $cache ]] && stale=1 + [[ -n $commands[tv] && $commands[tv] -nt $cache ]] && stale=1 + + if (( stale )); then + { + if (( $+commands[tv] )); then + tv init zsh + print + fi + if (( $+commands[dircolors] )); then + if [[ -r ~/.dircolors ]]; then + dircolors -b ~/.dircolors + else + dircolors -b + fi + print + fi + } > $cache.tmp.$$ 2>/dev/null && mv -f $cache.tmp.$$ $cache + rm -f $cache.tmp.$$ + zcompile $cache 2>/dev/null + fi + + [[ -s $cache ]] && source $cache +} + +# oh-my-posh cacht seinen Init-Code seit v26 selbst unter ~/.cache/oh-my-posh/ +# (Dateiname enthaelt Version + Config-Hash). "oh-my-posh init zsh" gibt nur +# noch einen kleinen Stub aus, der diese Datei sourct. Ein eigener Cache +# darueber bringt deshalb nichts und bricht, sobald sich Version oder Theme +# aendert - der Stub zeigt dann auf einen Pfad, den es nicht mehr gibt. +if (( $+commands[oh-my-posh] )); then + eval "$(oh-my-posh init zsh --config "$OMP_CONFIG")" +fi + +# Jetzt steht LS_COLORS -> Completion einfaerben +zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS} + +# Cache verwerfen und Shell neu starten +zshrc-refresh() { + rm -f $ZSH_CACHE_DIR/init.zsh{,.zwc} \ + $ZSH_CACHE_DIR/zcompdump{,.zwc} \ + ~/.zshrc.zwc + # oh-my-posh raeumt seinen eigenen Cache normalerweise selbst auf. + # Falls das Prompt nach einem Update zickt, zusaetzlich: + # rm -rf ~/.cache/oh-my-posh + echo "Cache geleert, starte Shell neu ..." + exec zsh +} +# ----------------------------------------------------------------------------- +# 5. PATH +# ----------------------------------------------------------------------------- +typeset -U path PATH # automatisch dedupliziert +path=( + $HOME/.local/bin # pip3 --user + $path + $HOME/go/bin + $HOME/.cargo/bin +) +path=($^path(N-/)) # nur existierende Verzeichnisse behalten +# ----------------------------------------------------------------------------- +# 6. Aliases +# ----------------------------------------------------------------------------- +alias ls='ls --color=auto' +alias grep='grep --color=auto' +alias fgrep='fgrep --color=auto' +alias egrep='egrep --color=auto' -if [ -x /usr/bin/dircolors ]; then - test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" - alias ls='ls --color=auto' - #alias dir='dir --color=auto' - #alias vdir='vdir --color=auto' +[[ -f ~/.bash_aliases ]] && source ~/.bash_aliases +[[ -f ~/.config/common_aliases ]] && source ~/.config/common_aliases - alias grep='grep --color=auto' - alias fgrep='fgrep --color=auto' - alias egrep='egrep --color=auto' + +# ----------------------------------------------------------------------------- +# 7. tmux-Fenstername = aktuelles Verzeichnis +# ----------------------------------------------------------------------------- +# Vorher via PROMPT_COMMAND - das ist eine Bash-Variable und hat in zsh nie +# funktioniert. Jetzt korrekt als chpwd-Hook: laeuft nur beim Verzeichnis- +# wechsel, nicht bei jedem Prompt. +if [[ -n "$TMUX" ]]; then + autoload -Uz add-zsh-hook + _tmux_rename_window() { tmux rename-window "${PWD:t}" 2>/dev/null } + add-zsh-hook chpwd _tmux_rename_window + _tmux_rename_window fi +# ----------------------------------------------------------------------------- +# 8. Funktionen +# ----------------------------------------------------------------------------- -if [ -f ~/.bash_aliases ]; then - . ~/.bash_aliases -fi - -if [ -f ~/.config/common_aliases ]; then - . ~/.config/common_aliases -fi - -# Determin Termanial Type -MYTERMINAL=$(basename "/"$(ps -o cmd -f -p $(cat /proc/$(echo $$)/stat | cut -d \ -f 4) | tail -1 | sed 's/ .*$//')) -# Attach to TMUX session if not already done and only if we're in xfce4-terminal (but not e.g. PyCharm) -if [ -z "$TMUX" ] && [ ${UID} != 0 ] && [[ "$MYTERMINAL" == "xfce4-terminal" ]] || [[ "$MYTERMINAL" == "kitty" ]] || [[ "$MYTERMINAL" == "ghostty" ]] || [[ "$MYTERMINAL" == "sh" ]] -then - #tmux new-session -A -s main - tmux attach || tmux new -fi - +# yazi mit cwd-Uebernahme function y() { - local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd - yazi "$@" --cwd-file="$tmp" - IFS= read -r -d '' cwd < "$tmp" - [ -n "$cwd" ] && [ "$cwd" != "$PWD" ] && builtin cd -- "$cwd" - rm -f -- "$tmp" + local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd + yazi "$@" --cwd-file="$tmp" + IFS= read -r -d '' cwd < "$tmp" + [ -n "$cwd" ] && [ "$cwd" != "$PWD" ] && builtin cd -- "$cwd" + rm -f -- "$tmp" } +# --- Bandcamp ----------------------------------------------------------------- -#### for pip3 --user installed applications -export PATH=$HOME/.local/bin:$PATH -export PATH="$PATH:/home/sebastian/go/bin:/home/sebastian/.cargo/bin" - - -function tmux_pane(){ - tmux rename-window "$(basename $PWD)" +function bandcampdl() { + yt-dlp \ + -x \ + --audio-format mp3 \ + --audio-quality 0 \ + --embed-metadata \ + --embed-thumbnail \ + --convert-thumbnails jpg \ + -o "%(album_artist)s/%(release_year)s - %(album)s/%(album_artist)s - %(album)s - %(track_number)02d - %(title)s.%(ext)s" \ + "$1" } -PROMPT_COMMAND="$PROMPT_COMMAND; tmux_pane" +function bandcampbatch() { + local file="$1" + while IFS= read -r url; do + [[ -z "$url" || "$url" == \#* ]] && continue + bandcampdl "$url" + done < "$file" +} -zstyle ':completion:*:*:ssh:*' hosts \ - ${(f)"$(grep -h '^Host ' ~/.ssh/config ~/.ssh/config.d/* 2>/dev/null | \ - sed 's/^Host //' | tr ' ' '\n')"} +function bandcamplist() { + local url outfile tmpfile band count - -eval "$(tv init zsh)" + command -v yt-dlp >/dev/null 2>&1 || { + echo "Fehler: yt-dlp wurde nicht gefunden." + return 1 + } + + if (( $# < 1 || $# > 2 )); then + echo "Usage:" + echo " bandcamplist [output.txt]" + return 1 + fi + + url="$1" + + if [[ ! "$url" =~ ^https?://([^.]+)\.bandcamp\.com/music/?$ ]]; then + echo "Fehler: Keine gueltige Bandcamp-/music-URL:" + echo " $url" + return 1 + fi + + band="${match[1]}" + + if [[ $# -eq 2 ]]; then + outfile="$2" + else + outfile="bandcamp-${band}.txt" + fi + + tmpfile=$(mktemp) + + if ! yt-dlp \ + --flat-playlist \ + --print webpage_url \ + "$url" \ + | sort -u > "$tmpfile" + then + rm -f "$tmpfile" + echo "Fehler beim Abrufen der Links." + return 1 + fi + + count=$(wc -l < "$tmpfile") + + if (( count == 0 )); then + rm -f "$tmpfile" + echo "Keine Album- oder Track-Links gefunden." + return 1 + fi + + mv "$tmpfile" "$outfile" + + echo "✓ $count Links nach '$outfile' geschrieben." +} + +# tm [name] -> Session anlegen oder hinwechseln (Default: main) +function tm() { + local s=${1:-main} + if [[ -n "$TMUX" ]]; then + tmux has-session -t "=$s" 2>/dev/null || tmux new-session -d -s "$s" + tmux switch-client -t "=$s" + else + tmux new-session -A -s "$s" + fi +} + +# ----------------------------------------------------------------------------- +# 9. Abschluss +# ----------------------------------------------------------------------------- + +# zshrc selbst kompilieren - zsh benutzt die .zwc-Datei automatisch, solange sie +# neuer ist als das Original. Nach jeder Aenderung wird sie hier neu gebaut. +if [[ ! -s ~/.zshrc.zwc || ~/.zshrc -nt ~/.zshrc.zwc ]]; then + zcompile ~/.zshrc 2>/dev/null +fi + +if [[ -n "$ZSHRC_PROFILE" ]]; then + zprof +fi